0%

CVE-2025-32756-Fortimail-stackoverflow

漏洞信息

CVE-2025-32756 是一个位于 API 中的,影响 FortiVoice, FortiMail, FortiNDR, FortiRecorder, FortiCamera 的栈溢出漏洞,它允许未授权的攻击者在系统上执行任意代码或者命令。

漏洞点

国外分析文章https://horizon3.ai/attack-research/attack-blogs/cve-2025-32756-low-rise-jeans-are-back-and-so-are-buffer-overflows/已经指出了漏洞点,位于动态链接库libhttputil.so中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if ( (unsigned int)__isoc99_sscanf(dest, "Era=%1d&Payload=%m[^&]&AuthHash=%m[^&]&", &v34, &Payload, &AuthHash) != 3 )
{
v29 = -1;
v3 = AuthHash;
v8 = 0;
v4 = 0;
goto LABEL_15;
}
....
if ( v9 )
{
EVP_DecodeInit(v9);
v35 = 20;
v43.m128i_i32[0] = 20;
if ( (int)EVP_DecodeUpdate(v10, &v41, &v35, AuthHash, (unsigned int)v5) >= 0 )
{

我们可以发现他会从Cookie中提取AuthHash的值,并对其进行解密,进一步查看EVP_DecodeUpdate,发现就是一个base64解码。同时这里没有对AuthHash的长度进行检查,并且是直接朝栈上进行的拷贝,所以很显然这里存在一个栈溢出。

环境搭建

直接导入镜像,然后把网络适配器从桥接改为NAT。之后开启虚拟机,等待它进行初始化。

初始化完成之后可以通过,admin/空密码进入后台,这时会提示重新设置密码。

之后按照下列命令配置网络和开启服务即可。

1
2
3
4
5
6
config system interface
edit port1
set mode static
set ip 192.168.217.121 255.255.255.0
set allowaccess ping ssh telnet http https
end

尝试访问https://192.168.217.121,出现登录框即可说明环境搭建成功。

shell获取

fortimail的vmdk挂载之后之后有一个/etc目录,启动时并不会检查这个目录完整性,可以对这个目录进行修改,在里面增加busybox,gdbserver等文件。

同时修改/etc/vmware-tools/powerops.sh这个脚本即可获得shell。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/sh
/etc/busybox killall sshd && /etc/busybox telnetd -l /etc/sh -b 0.0.0.0 -p 22
exit

case "$0" in
*poweroff*)
;;

*poweron*)
;;

*resume*)
;;

*suspend*)
;;

*)
;;
esac
return 0;

漏洞利用

因为这个漏洞的触发点是admin.fe这个cgi,并且我不想去分析httpd找出创建cgi进程的部分,所以我通过下列命令去附加admin.fe进程。

1
2
3
4
5
while :; 
do
/etc/busybox kill -9 $(/etc/busybox ps| /etc/busybox grep /bin/telnetd| /etc/busybox grep -v grep| /etc/busybox awk '{print $1}')
/etc/gdbserver 0.0.0.0:23 --attach $(/etc/busybox ps| /etc/busybox grep 'admin.fe'| /etc/busybox grep -v grep| /etc/busybox awk 'NR==1{print $1}');
done

查看保护会发现他只开了一个NX,但实际上调试的时候发现他的栈是可执行的,所以相当于什么保护都没有开了。

这里的利用方法第一个肯定想到的是直接进行ROP。可是admin.fe这个程序很小,没有涉及到system等命令执行函数,这无疑给我们的利益带来了一些困难。后来想到的利用思路大概有以下几个。

ret2shellcode

因为调试发现栈具有RWX权限,所以我们可以想办法通过jmp rsp等方式,将执行流劫持到栈上,并提前布置好shellcode。但是在经过我的搜索之后,并没能发现合适的gadget,故放弃这一方法。

部分覆盖got表

由于我们程序中调用了memcpy这个函数,故我们可以通过布置gadget去活得任意地址写的能力,我们可以尝试部分写已经解析的函数的got表的末尾几位去将其覆盖为system这个函数,再进行ROP构造去进行RCE。但是实际调试发现,所解析的函数在libc中距离system函数比较远,想通过这种方式需要覆盖3字节,除了末尾三位是确定的,还会存在三位不确定部分。这就导致只有1/4096的概率成功。于是我们优先考虑其他更稳定的利用方式。

ret2syscall

在admin.fe中可以凑到syscall这个指令,所以我们只要布置好几个参数再调用即可实现RCE。

这里给出ROP大致思路:

第一步,调用memcpy将execve的第二个参数的字符串复制到bss段,bss1,bss2…。

第二步,调用memcpy将第一次复制的各字符串的地址复制到bss段,bss_3。

第三步,调用通过找到pop rdi,pop rsi,pop rax等gadget控制好rdi,rsi,rax,之后调用syscall即可。

ret2dlresolve

由于我们具有任意地址写的能力,此外RELRO的保护没开,所以我们可以通过伪造dynstr,并修改.dynamic 段中所存的dynstr指针指向我们伪造的dynstr即可。

这里给出ROP大致思路:

第一步,调用memcpy将原本dynstr正常的部分复制到bss段,bss1。

第二步,调用memcpy将需要伪造的部分覆盖到bss1某处偏移处,同时设置好system要执行的参数。

第三步,调用memcpy将.dynamic 段中所存的dynstr指针指向我们伪造的dynstr,及bss1。

第四步,调用通过找到pop rdi控制好rdi,之后再次出发相关函数的runtime resolve即可。

参考链接

https://wzt.ac.cn/2025/06/03/CVE-2025-32756/

https://horizon3.ai/attack-research/attack-blogs/cve-2025-32756-low-rise-jeans-are-back-and-so-are-buffer-overflows/