如往常,細節能隱藏在更大的圖像中。單一主機通常存在於某種基礎架構之內。

連線本身就是紅旗#

入侵偵測系統(IDS)與入侵防禦系統(IPS)能偵測異常網路流量;連路由器與防火牆上的簡單日誌,都能揭露指示入侵的異常連線。特別是我們反連 shellcode 用的連接埠 31337 是一面大紅旗

改用不那麼可疑的連接埠也不夠——一個網頁伺服器發起連出連線,本身就可能是紅旗。高度安全的架構甚至可能用防火牆的出口過濾(egress filter)阻止連出連線,此時開新連線不是不可能、就是會被偵測。

重用 socket#

我們其實根本不需要開新連線——網頁請求已經有一個開著的 socket。既然正在 tinyweb daemon 內部搗弄,稍加除錯就能重用既有的 socket 來提供 root shell:這既避免額外 TCP 連線被記錄,也讓「目標主機無法開連出連線」的情況仍可利用。

不幸的是,傳給 handle_connection()sockfd 終究會被覆寫(好讓我們覆寫 logfd),而這覆寫發生在我們於 shellcode 中取得控制之前,無法還原 sockfd 舊值。所幸 main()new_sockfd 保留了 socket 檔案描述元的另一份副本。

用 GDB 發現 new_sockfd 存在 main() 堆疊框架的 0xbffff83c

可以直接用這個位址,但許多小事會讓堆疊記憶體移位——硬編碼堆疊位址會讓漏洞利用失敗。取法編譯器處理堆疊變數的方式:用相對 ESP 的位址。 即使堆疊稍有移位,相對 ESP 的偏移不變,new_sockfd 位址仍正確。已知 ESP 為 0xbffff7e0,偏移是 0x5c

child_process:
  ; 重用既有 socket
  lea edx, [esp+0x5c] ; new_sockfd 的位址
  mov ebx, [edx]      ; new_sockfd 的值放進 ebx
  push BYTE 0x02 / pop ecx    ; ecx 從 2 開始
  xor eax, eax / xor edx, edx
dup_loop:
  mov BYTE al, 0x3F   ; dup2 syscall #63
  int 0x80
  dec ecx
  jns dup_loop
  ; 接著 execve /bin//sh

保持 socket 開著以供互動#

要有效使用這段 shellcode,需要一個能送出漏洞利用緩衝區、又保持 socket 供後續 I/O 的工具。xtool_tinywebd_reuse.sh 在緩衝區尾端加 cat -cat 標準輸入本身沒用,但管線進 netcat 後,就把標準輸入輸出綁到 netcat 的網路 socket——連上目標、送緩衝區、然後保持 socket 開著並從終端取後續輸入。

reader@hacking:~/booksrc $ ./xtool_tinywebd_reuse.sh socket_reuse_restore 127.0.0.1
...
whoami
root

藉由重用既有 socket,這個漏洞利用更安靜——它不建立任何額外連線。連線越少,對策能偵測到的異常就越少。