在 CTF 競賽中,Pwn 這個詞源自駭客俚語「Pwned」(意指完全掌控、攻陷系統)。Pwn 專注於挖掘與利用二進位程式中的記憶體安全漏洞(Memory Corruption Vulnerabilities),最終目標通常是奪取目標主機的 Shell 權限並讀取 Flag。
在眾多漏洞類型中,最經典、歷史最悠久也最基礎的莫過於 緩衝區溢位 (Buffer Overflow, BOF)。要理解 BOF,我們必須先看懂程式在電腦記憶體中的長相。
當一個 C 語言程式被載入作業系統執行時,作業系統會在記憶體中為它分配一塊連續的空間,其經典的虛擬記憶體佈局(由低位址到高位址)如下:
malloc 或 new)。Stack(堆疊) 的運作機制是後進先出 (LIFO)。當進入一個函數時,系統會在堆疊上開闢一個空間(Stack Frame)給區域變數。
如果程式在接收使用者輸入(例如使用不安全的函數如 strcpy, gets, 或未限制長度的 scanf)時,沒有檢查輸入的長度,當輸入的資料大小超過原本配置的緩衝區 (Buffer) 大小:
ret) 時,CPU 會把被篡改的返回地址當成下一個執行指令的位址跳過去——這就是劫持程式控制流 (Control Flow Hijacking) 的由來!我們在 WSL 中親自寫一個存在經典緩衝區溢位漏洞的 C 程式,並觀察其崩潰 (Segmentation Fault) 現象。
在 WSL 終端機中執行以下命令:
# 1. 建立具有漏洞的 C 原始碼
cat << 'EOF' > bof_demo.c
#include <stdio.h>
#include <string.h>
void vulnerable_function() {
char buffer[16]; // 僅分配 16 bytes 的緩衝區
printf("Enter your input: ");
gets(buffer); // 極度危險的函數,不檢查輸入長度!
printf("You entered: %s\n", buffer);
}
int main() {
printf("=== Buffer Overflow Demo ===\n");
vulnerable_function();
printf("Program finished normally.\n");
return 0;
}
EOF
# 2. 編譯 C 程式 (關閉部分防護以便觀察溢位,如 Stack Canary 與棧不可執行)
gcc -fno-stack-protector -z execstack bof_demo.c -o bof_demo

我們分別用「正常長度」與「超長字串」來執行剛才編譯好的二進位檔:
# 測試 1:正常輸入 (小於 16 bytes)
./bof_demo
# 輸入:Hello

輸出結果:程式正常結束。
# 測試 2:超長輸入(引發緩衝區溢位)
python3 -c "print('A' * 40)" | ./bof_demo
輸出結果:你會發現終端機印出 Segmentation fault (core dumped)!這代表輸入的 40 個 'A' 成功撐爆了 16 bytes 的緩衝區,並一路向上覆蓋了返回地址,導致 CPU 試圖跳去執行記憶體位址 0x41414141('A' 的 ASCII 十六進位)而當機。
今天我們透過手動寫出漏洞程式並用長字串灌爆堆疊,親眼見證了 Segmentation Fault 的誕生。這正是 Pwn 攻擊中最核心的「覆蓋返回地址」原理。
不過現代作業系統為了防止這類攻擊,內建了許多強大的防禦機制: