緩沖區(qū)溢出概述
緩沖區(qū)溢出(Buffer Overflow)是一種經(jīng)典的安全漏洞,當(dāng)程序未對(duì)輸入長(zhǎng)度進(jìn)行檢查時(shí),多余的數(shù)據(jù)會(huì)覆蓋相鄰內(nèi)存區(qū)域,進(jìn)而篡改程序控制流,達(dá)到執(zhí)行任意代碼的目的。
1. 棧(Stack)與堆(Heap)
- 棧 (Stack):后進(jìn)先出(LIFO)結(jié)構(gòu),用于管理函數(shù)調(diào)用。每次調(diào)用都會(huì)創(chuàng)建獨(dú)立棧幀,包含函數(shù)參數(shù)、返回地址、保存的寄存器(如 EBP)和局部變量。
- 堆 (Heap):用于動(dòng)態(tài)分配內(nèi)存,地址從低向高增長(zhǎng),需手動(dòng)分配和釋放。堆與棧分區(qū)相互獨(dú)立。
2. 字節(jié)序(Endianness)
在多字節(jié)系統(tǒng)中:
- 大端序 (Big-Endian):高位字節(jié)存儲(chǔ)在低地址,常見(jiàn)于網(wǎng)絡(luò)協(xié)議(如 IP 頭)。
- 小端序 (Little-Endian):低位字節(jié)存儲(chǔ)在低地址,x86、ARM 默認(rèn)采用此模式。
示例:32 位地址 0xbffffb80
在小端系統(tǒng)中的存儲(chǔ)順序:
內(nèi)存地址增長(zhǎng) → 低地址: 0x80 0xfb 0xff 0xbf
在利用漏洞時(shí),必須將地址按小端序?qū)懭胼d荷(如 \x80\xfb\xff\xbf
),否則會(huì)跳轉(zhuǎn)到錯(cuò)誤地址。
3. x86 棧幀布局與函數(shù)調(diào)用
高地址
+------------------+
| 參數(shù) n | ← 調(diào)用者按右→左順序壓棧
| ... |
+------------------+
| 返回地址 (EIP) | ← `call` 指令自動(dòng)壓棧
+------------------+
| 舊 EBP | ← `push ebp`
+------------------+ ← 新 EBP 指向此處
| 局部變量 | ← `sub esp, N`
| ... |
低地址
調(diào)用流程:
- 調(diào)用者按反序壓入?yún)?shù)。
call
將返回地址壓棧,并跳轉(zhuǎn)到函數(shù)入口。push ebp; mov ebp, esp; sub esp, N
設(shè)置新棧幀。leave; ret
恢復(fù) EBP 并將返回地址彈棧到 EIP。
4. 緩沖區(qū)溢出攻擊流程
以以下易受攻擊函數(shù)為例:
void vulnerable(char *input) {
char buffer[256];
strcpy(buffer, input);
}
- 當(dāng)
input
超過(guò) 256 字節(jié),多余數(shù)據(jù)向高地址寫(xiě)入,依次覆蓋:舊 EBP → 返回地址 → 參數(shù)區(qū)。 - 覆蓋返回地址后,執(zhí)行
ret
時(shí),EIP 跳轉(zhuǎn)到攻擊者指定地址。 - 若該地址指向包含 shellcode 的輸入?yún)^(qū),即可實(shí)現(xiàn)任意代碼執(zhí)行。
5. 構(gòu)造利用載荷
./vuln $(python -c 'print "A"*268 + "\x80\xfb\xff\xbf" + "\x90"*20 + SHELLCODE')
- 偏移量:通過(guò) GDB 找到 EIP 覆蓋點(diǎn),此處為 268。
- 小端地址:
\x80\xfb\xff\xbf
對(duì)應(yīng)內(nèi)存地址 0xbffffb80
。 - NOP 雪橇:
\x90
填充,覆蓋返回地址與 shellcode 之間的區(qū)域,擴(kuò)大跳板范圍。
6. 獲取 ESP 地址的方法
方法一:EIP 覆蓋測(cè)試
$(python -c 'print "A"*268 + "BBBB"')
,觀察 GDB 崩潰時(shí):返回地址是否被 0x42424242
(BBBB
)替換。- 若匹配,說(shuō)明偏移 268 后正好覆蓋返回地址。
方法二:ESP 跳板測(cè)試
在偏移基礎(chǔ)上繼續(xù)追加 NOP 和標(biāo)記:
$(python -c 'print "A"*268 + "BBBB" + "C"*20')
當(dāng) EIP 被 BBBB
覆蓋后,程序崩潰時(shí)返回地址已彈棧,GDB 顯示 ESP 指向 CCCC...
區(qū)域,即 shellcode 起始處。
該地址即為跳轉(zhuǎn)目標(biāo),可按小端序?qū)懭敕祷氐刂贰?/p>
注意:多次測(cè)試時(shí),若輸入長(zhǎng)度變化(如從 272 → 292 字節(jié)),操作系統(tǒng)會(huì)為對(duì)齊在更低地址分配新的??臻g,導(dǎo)致 ESP 地址出現(xiàn)偏移差異。
7. NOP 雪橇與其作用范圍
- 目的:在返回地址到 shellcode 區(qū)間填充
\x90
,形成?NOP
?滑板,確保 EIP 落點(diǎn)可滑入真實(shí) shellcode。 - 長(zhǎng)度:通常幾十到數(shù)百字節(jié),根據(jù)偏移范圍動(dòng)態(tài)調(diào)整。
8. 壞字符測(cè)試(Bad Character Testing)
使用腳本生成連續(xù)字節(jié)串:
python -c 'print "".join([chr(i) for i in range(1,256)])'
將該字符串傳入程序,在 GDB 中查看內(nèi)存(x/256x $esp
或 x/256b $esp
)。
若發(fā)現(xiàn)某字節(jié)丟失或被解釋異常,則需將其從 payload 中排除,直到所有字節(jié)都能正確通過(guò)。
9. 常見(jiàn)調(diào)用約定對(duì)比
約定 | 參數(shù)順序 | 清理者 | 示例 |
---|
cdecl | 右→左 | 調(diào)用者 | C 默認(rèn) |
stdcall | 右→左 | 被調(diào)用者 | Windows API |
fastcall | 前幾個(gè)通過(guò)寄存器 | 被調(diào)用者 | 性能優(yōu)化 |
10. x86 與 x64 區(qū)別
- 寄存器寬度:x86 為 32 位,x64 為 64 位。
- 新增寄存器:x64 增加 R8–R15。
- 參數(shù)傳遞:x64 多數(shù)通過(guò)寄存器,棧負(fù)載減少。
以上內(nèi)容整合了棧與堆、字節(jié)序、棧幀結(jié)構(gòu)、利用流程、偏移與跳板測(cè)試、NOP 雪橇、壞字符測(cè)試及調(diào)用約定差異,建議結(jié)合 GDB 工具逐步驗(yàn)證。
該文章在 2025/6/3 9:14:13 編輯過(guò)