CVE-2025-32756

2025 年 5 月 13 日,Fortinet 发布了影响 FortiMail、FortiVoice 等系统的远程代码执行漏洞预警公告 CVE-2025-32756,本文对此漏洞进行简要分析。
该漏洞已发现在野利用,建议受影响的用户及时更新系统版本。
本文有部分内容是大模型自动生成的,涉及的部分会以 🤖 来标记
基本信息
Fortinet 的漏洞预警公告中指出该漏洞是一个位于 API 中的,影响 FortiVoice, FortiMail, FortiNDR, FortiRecorder, FortiCamera 的栈溢出漏洞,它允许未授权的攻击者在系统上执行任意代码或者命令。
同时公告中还给出了详细的 IoC 信息,当这个漏洞被利用时,会在系统日志中留下进程崩溃信息:
1 | [x x x x:x:x.x 2025] [fcgid:warn] [pid 1829] [client x.x.x.x:x] mod_fcgid: error reading data, FastCGI server closed connection |
从日志中可以看到,Web 服务中的 mod_fcgid 模块与 CGI 之间的连接中断了,并且 CGI 程序 admin.fe 由于段错误退出。
通过这些信息能够推测出:
- 系统的 Web 服务使用了 Apache httpd 或者类似的 Web server
- 漏洞位于 CGI 程序中,能够确定 admin.fe 是一个触发点,但也可能可以通过其它接口触发
- 漏洞类型为栈溢出,能够执行命令说明目标程序没有开启必要的缓解措施
补丁对比
公告提到在 FortiVoice 中检测到在野利用,那么我们就用这套系统进行补丁对比。获取到 FortiVoice 7.0.6 和 7.0.7 两个 VM 版本镜像分析。
首先要解包系统文件,和以往的思路一样,先挂载磁盘。FortiVoice 中的 rootfs.gz 等压缩包没有经过加密,可以直接解压得到根文件系统。同时可以在 etc 目录下找到 Apache httpd 的配置文件,其中一项关键配置是
1 | Alias /module /migadmin/www/fcgi/ |
这条规则将发往 /module 的请求映射到 /migadmin/www/fcgi/ 目录下,刚好是 IoC 中提到的 CGI 文件所在目录。比较两个版本的 CGI 目录,只有三个程序发生了变化,但 admin.fe 是相同的
逆向分析 admin.fe 二进制程序,它的 main 函数如下
1 | void main(uint param_1,char **param_2) |
CGI 程序自身没有实现业务逻辑,而是会大量引用链接库中的函数,通过搜索函数名,在 /lib 目录下找到一系列相关的链接库文件,通过进一步分析可以确定,和漏洞相关的关键逻辑位于 libhttputil.so 中。
对两个版本的文件进行补丁对比,libhttputil.so 主要是 cookieval_wrap
和 cookieval_unwrap
发生了变化:
新版本对某个参数的长度进行了检查,要求它的长度不能超过 0x19 字节,下面是 cookieval_unwrap
函数的部分代码:
1 | if ( cookie ) |
函数开头会使用 sscanf 处理传入的参数,按照格式取出其中的三个参数,分别是 Era、Payload 和 AuthHash,这刚好是请求中的 Cookie 参数内容,而新版本限制了 AuthHash 参数的长度。
漏洞分析(🤖)
我们尝试使用大模型自动分析可能的漏洞成因,并且自动构造 poc 请求,这里使用 DeepSeek-R1 模型。
🤺:现在我正在复现某个 CVE 漏洞,我通过二进制补丁对比找到了可能和这个漏洞有关的函数,请你分析新旧两个版本的函数,告诉我漏洞的成因是什么,并且构造一个 poc 数据。(新旧版本 cookieval_unwrap 函数代码)
🤖:通过分析新旧两个版本的 cookieval_unwrap
函数,我发现了一个关键的安全漏洞及其修复方案。以下是详细分析:
漏洞成因分析
问题核心:Base64解码缓冲区溢出
- 旧版本中未对
AuthHash
参数的长度进行限制 - 在 Base64 解码时(
EVP_DecodeUpdate
调用),使用原始长度authhash_len = strlen(authhash)
- 当
AuthHash
值超过 24字符时,Base64解码后数据会超出目标缓冲区out[32]
的容量(32字节)
- 旧版本中未对
关键漏洞代码(旧版本):
1
2
3authhash_len = strlen(authhash); // 无长度限制
// ...
if (EVP_DecodeUpdate(v21, out, &out_len, authhash, authhash_len) < 0)修复方案(新版本):
1
2
3
4
5
6n24 = 24; // 新增长度限制
if (authhash_len <= 24)
n24 = authhash_len;
LODWORD(authhash_len_1) = n24; // 限制后的长度
// ...
if (EVP_DecodeUpdate(v23, out, &out_len, authhash, authhash_len_1) < 0)
POC构造
1 | #!/usr/bin/env python3 |
POC使用说明
执行POC脚本:
1
python3 poc.py
预期输出:
1
2
3
4
5恶意Cookie:
Era=0&Payload=legit_data&AuthHash=QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUE=
AuthHash长度: 68 (>24 触发漏洞)
✅ 可触发缓冲区溢出漏洞触发效果:
- 当程序处理该Cookie时
EVP_DecodeUpdate
尝试将68字符Base64解码为48字节数据- 超出
out[32]
缓冲区16字节 → 栈缓冲区溢出 - 可能导致程序崩溃或任意代码执行(取决于溢出内容控制)
技术细节
安全边界:
- Base64解码后大小公式:
ceil(n/4)*3
- 24字符Base64 → 最大18字节输出(小于32字节缓冲区)
- 68字符Base64 → 输出51字节(超过32字节缓冲区)
- Base64解码后大小公式:
攻击向量:
- 攻击者可通过伪造恶意Cookie触发漏洞
- 结合ROP链可实现远程代码执行
- 影响身份验证相关的cookie处理流程
此漏洞属于典型的缺少输入验证导致的栈缓冲区溢出(CWE-121),建议对所有外部输入进行严格长度校验。
可以看到大模型对漏洞的成因理解是正确的,并且也给出了可以用于测试的 poc 代码,但是由于反编译代码存在信息丢失,脚本中溢出到返回地址需要的 padding 长度存在误差。
漏洞利用
以 FortiMail 7.2.2 版本为例,来分析一下该漏洞利用的可行性。使用前面的脚本生成 poc 请求发送到 web 服务,可以在调试器中捕获到这样的崩溃信息
此时 rip 指向 ret 指令,同时 rsp 中的内容是可控的,所以能够劫持程序控制流。而且溢出的数据是经过 Base64 编码的,可以写入 NULL 字节,但是还有几个问题需要解决
- 系统开启了 ASLR,CGI 进程每次崩溃地址都会变化
- stack 等区域的内存是具有 rwx 权限的,但存在于已知地址中的 gadget 难以将控制流转移到 stack 上面
考虑到各方面的限制条件,我们可以选择使用 ret2dlresolve 技巧实现利用,因为这种方法能直接解析任意符号并调用,无需爆破地址,另外 CGI 程序的 RELRO 为 NO RELRO,这也降低了利用 ret2dlresolve 的难度。关于 ret2dlresolve 的方法网络上有很多文章,在这里不再赘述。
利用 ret2dlresolve 需要满足一些前提条件,程序没有重定位保护所以 .dynamic 段是可写的,那么只需要一个已知地址可以保存我们伪造的 dynstr 结构。由于程序引用了 memcpy 函数,通过使用合适的 gadget 能够实现将 stack 上可控的数据拷贝到一个已知地址。那么利用思路可以总结为:
- 寻找到合适的 gadget,调用 memcpy 函数将伪造的 dynstr 以及需要执行的命令拷贝到一个已知地址
- 调用 memcpy 函数修改 dynamic 段中的 dynstr 指针,指向我们伪造的结构
- 触发 runtime resolve,同时设置好参数,调用 system 函数执行任意命令
参考链接
- Title: CVE-2025-32756
- Author: Catalpa
- Created at : 2025-06-03 00:00:00
- Updated at : 2025-06-03 15:26:17
- Link: https://wzt.ac.cn/2025/06/03/CVE-2025-32756/
- License: This work is licensed under CC BY-SA 4.0.