堆疊破壞(stack smashing)
如果堆疊緩衝區溢位是「溢出」這個現象,堆疊破壞就是把這個溢出刻意武器化:安排溢出的位元組,讓它們剛好落在函式的返回位址上,並換成攻擊者選定的位址。這個說法來自 1996 年一篇著名文章《Smashing the Stack for Fun and Profit》,它一步步把整套技術攤開來講。
把機制走一遍。在典型向下成長的堆疊上,一個函式的堆疊框(由低位址到高位址)放著它的區域緩衝區,接著是被儲存的框指標(rbp),再來是被儲存的返回位址——ret 指令會把這個值彈進 rip 以回到呼叫者。一個寫過區域緩衝區的溢位,會沿著記憶體一路往上朝這些被儲存的值前進。攻擊者把輸入墊得剛好,讓覆蓋返回位址的那些位元組構成一個指向他們想執行之程式碼的指標:在最初的攻擊中,指向他們同樣放進緩衝區的 shellcode;在現代攻擊中(因為 DEP/NX),指向一條返回導向程式設計(ROP)鏈的起點。當函式執行 ret 時,它就跳到攻擊者的位址,而不是回到合法的呼叫者。
它之所以重要,是因為「控制返回位址」正是從記憶體漏洞通往完整控制流劫持的標準路徑。誠實的提醒:如今幾乎每個現代編譯器與作業系統都在防守這個確切位置。堆疊金絲雀坐在區域變數與返回位址之間,會在 ret 之前被檢查;ASLR 把位址隨機化,讓攻擊者不知道該指向哪裡;DEP 讓緩衝區不可執行。所以教科書式的堆疊破壞如今很少能原封不動奏效——但這類漏洞仍然存在,把它串接過這些緩解措施才是真正的功夫。
[ buf(16 位元組)][ 被儲存的 rbp(8)][ 返回位址(8)] AAAAAAAAAAAAAAAA AAAAAAAA BBBBBBBB <- 溢位 ^ 返回位址現在 = 0x...BBBBBBBB
墊料填滿 buf 與被儲存的框指標,最後的位元組落在(並改寫)返回位址上。
執行期出現的「stack smashing detected」是堆疊金絲雀攔截到時印出的訊息——它代表某個緩解措施「觸發了」,而不是程式本身在設計上就安全。