系統安全與漏洞利用

控制流劫持(control-flow hijacking)

程式是一份 CPU 逐行照著走的腳本,而程式計數器(x86-64 上的 rip)是那個書籤,指明下一條指令是哪一條。正常情況下只有程式自己的邏輯——透過呼叫、返回與分支——會移動那個書籤。控制流劫持就是攻擊者利用某個記憶體漏洞,自己去設定那個書籤,於是 CPU 開始執行攻擊者選定的指令,而不是程式原本的路徑。

把它理解成那些記憶體損毀漏洞所「服務的目標」最為恰當。CPU 會服從的每一個程式碼指標都是目標:堆疊上被儲存的返回位址(由堆疊溢位觸及)、結構裡的函式指標(由堆積溢位、釋放後使用或型別混淆觸及)、C++ 的 vtable 指標、或全域偏移表(GOT)裡的某個項目。劫持就是把這些之一改寫成攻擊者選定的位址。一旦某個 ret、間接呼叫(call rax)或間接跳躍用上被損毀的指標,rip 就移到攻擊者的位址,執行流便歸他們所有。他們把它指「向」什麼則一路演進:起初指向緩衝區裡注入的 shellcode,而在 DEP 讓資料不可執行之後——透過返回導向程式設計或 return-to-libc 指向既有的程式碼。

它之所以重要,是因為控制流劫持是「崩潰」與「淪陷」之間的分界線:一個只損毀資料的漏洞或許只讓程式行為失常,但一個能改寫執行流的漏洞,能以程式的權限執行任意程式碼。誠實的說法:現代的緩解措施正是圍繞這一步組織起來——堆疊金絲雀保護返回位址,控制流完整性與影子堆疊檢查間接轉移只能去合法目標,指標驗證對程式碼指標簽章,使偽造的指標會被偵測。這些都沒有修掉底層的記憶體漏洞;它們試圖讓劫持即使在漏洞觸發時也失敗。

// 以下任一者一旦被損毀,使用時就會改向 rip: ret // 把一個(被改寫的)返回位址彈進 rip call qword [rax] // 透過一個(被改寫的)函式指標做間接呼叫 jmp qword [vtable] // C++ 虛擬分派,透過一個(被改寫的)vtable 槽位

劫持瞄準的是少數幾條會從攻擊者可損毀之記憶體取得 rip 的指令。

崩潰與劫持來自「同一類」漏洞;損毀究竟停在拒絕服務,還是變成執行程式碼,取決於攻擊者能否操縱某個程式碼指標——而那正是 CFI、影子堆疊與 PAC 試圖阻止的事。

又稱
control-flow redirectionPC hijackEIP/RIP control劫持控制流改寫程式計數器