原文链接:https://bestwing.me/CVE-2022-42475-FortiGate-SSLVPN-HeapOverflow.html
TL; DR
这两天和 @leommxj 一起分析了和写了一下 CVE-2023-27997 的漏洞利用, 顺便一想想 CVE-2022-42475 这个漏洞也过去蛮久的了,于是准备把这篇 CVE-2022-42475 漏洞分析分享出来。注:本文不含完整的漏洞利用脚本。
下图为 CVE-2023-27997 的利用录屏, 与本文要讲的 CVE-2022-42475 无关
I learned a lot from @cfreal_ , and it's great to write exploits together with @leommxj.#CVE-2023-27997 pic.twitter.com/nEFndgvoVD
— swing (@bestswngs) June 17, 2023
注:这篇笔记成文于 2023-02-28 , 发表于 2023-06-18
正文
2022 年 12 月 12 日,Fortinet 官方发布了影响 FortiGate SSLVPN 的 RCE 漏洞 CVE-2022-42475 相关信息。本文对此漏洞的成因进行分析。
环境配置
测试版本为 7.2.2, 环境的安装和部署可以参考这篇文章: https://blog.csdn.net/meigang2012/article/details/87903878。 另外感谢下 @explorer 网管大哥在部署环境上的帮助。
导入虚拟机后需要配置下网络, 参考 https://docs.fortinet.com/document/fortigate-private-cloud/7.2.0/vmware-esxi-administration-guide/615472/configuring-port-1
有个重点 dns 要设置下:
1 | config system dns |
需要配置一个 sslvpn ,然后能访问即可。
设备权限获取
挂载虚拟机 vmdk 硬盘后, 可以看到有个 rootfs.gz 文件。
1 | root@Jas-22:/home/user/Desktop/fuck-fortigate/rootfs |
可以看到有如下内容, 我们需要进一步解压 bin.tar.xz
文件夹,使用 sbin 目录自带的命令解压
1 | chroot . /sbin/xz --check=sha256 -d /bin.tar.xz |
然后需要在 bin 目录中放入后门,第一个是生成一个反弹shell替换 smartctl 文件, 以及我这里放入一个 busybox ,做一个软链接 ln -sn /bin/busybox bin/sh
设备中默认没有 bash (sh)文件 (或者说他的 sh 功能比较鸡肋), 然后重新打包。
1 |
|
重打包完成后, 我们需要过几个校验,才能正常启动系统。
vmlinux :
解下来是 bin/init:
这里会校验 fgtsum , 失败直接给你重启最后是 rootfs 检查
由于,我是采用 vmware + gdb 的调试方式, 即 使用VMware和GDB进行Linux内核调试 (bestwing.me), 因此我直接写了一个 gdb python 脚本动态修改返回值即可:
这里皮一句,依稀记得这段代码是 chatGPT 帮我生成的。
1 | import gdb |
当系统成功执行后,使用 diagnose hardware smartctl
即可运行我们的后门文件。
漏洞分析
Root Cause
在处理用户 POST 数据的时候,
会根据 http header 中的 content-length
字段分配 buffer , 然而在分配之前, 即在调用 pool_alloc 函数之前
pool_alloc 有两个参数, 第二个为即将要分配的buffer 大小
rax 为用户请求结构体指针,偏移位置 0x18 存放了 CL 值。先将 CL 放在 eax 寄存器中,使用 lea 指令将其加一后放在 esi 寄存器,再用 movsxd 扩展为 64 bit 值。
在调用 pool_alloc 函数时使用 32 位数值 + 1 拓展成 64 位的方法,这里存在整数溢出。那么我们可以构造特殊的 CL 值,比如 0x1b00000000,经过运算拓展之后会变成 0x1 。会分配一个小的内存空间导致溢出
上面这个是断点是初始化 buffer , 可以看到大小是 1, 之后在 memcpy 处就 会溢出。
exploit
这里首先明确一下,我不会公开完整的利用,这里这提一点利用上的思路。利用整体思路参考 Orange 2017 年的文章, 大致思路就是进行进行竞争, 一边在堆上布局 SSL 结构体,一边触发漏洞,然后溢出覆盖 SSL 结构体。
之后就可以控制 PC , 当我们控制 PC 后我们需要确定 padding , 这个步骤比较繁琐, 我拿 PoC 改了一个循环 fuzz 的脚本。
1 |
|
然后对 ret 这个gadget 下一个断点, 当触发断点的时候, 脚本会因为timeout 触发异常,然后这附近大概就是咱们的padding。
然后这个时候只需要找栈迁移的gadget 即可。这里我找了的 push rdx ; add bl, byte ptr [rbx + 0x41] ; pop rsp ; pop rbp ; ret
, 这个gadget , 正好可以将栈迁移到 rdi 寄存器所指向的内存地址上。然后将剩下 ret 指令的替换成 pop rax ; ret
这样的gadget, 这样就能一直迁移到可控制的 AAAAAAA
的地方进行 rop 链了。
再阅读这一部分的内容,我突然反应过来其实不需要替换指令, 当前的 ret 指令就足够迁移到可控的
AAAA
的位置进行 ROP 了
由于 fortigate 的这个程序很大,正如 CVE-2023-27997 的作者所说的,
该程序很大,想找到适合的 gadget 来组成 ropchain 仅仅需要花费一点时间就行了。
参考连接
Configuring port 1 | FortiGate Private Cloud 7.2.0 (fortinet.com)
attacking-ssl-vpn-part-2-breaking-the-Fortigate-ssl-vpn
使用VMware和GDB进行Linux内核调试 (bestwing.me)