Intel CET緩解機制實戰解讀

雲鼎實驗室發表於2022-03-14

前言

CET(CONTROL-FLOW ENFORCEMENT TECHNOLOGY)機制是 Intel 提出的⽤於緩解 ROP/JOP/COP 的新技術。因其具備“圖靈完備”的攻擊效果,ROP ⼀直是漏洞利⽤領域經常使⽤的攻擊技術,在漏洞防禦⽅⾯,針對 ROP 攻擊技術也不斷地在做新的嘗試。例如微軟的 CFG 緩解技術,雖然能夠起到⼀定的緩解效果,但是在複雜場景的攻擊下還不⾜夠。CET 是⼀項基於硬體⽀持的解決⽅案,旨在預防前向( call/jmp )和後向( ret )控制流指令劫持。

本⽂將從 CET 的設計理念和實際效果出發,探索 CET 技術在攻防上帶來的新變化。

 

0x01 ROP

ROP 全稱為 Return-oriented programming ,是⼀種⾼級的記憶體攻擊技術,且這種攻擊技術難以檢測。因為它利⽤了程式本身擁有的程式碼來執⾏精⼼構造的程式碼鏈。

ROP 依賴 RET 指令將多個段間的程式碼拼接在⼀起,組成⼀組完整的惡意程式碼。利⽤這個攻擊⽅式前需要擁有覆蓋返回地址的能⼒,其次攻擊者再從程式程式碼段中去尋找可利⽤的程式碼序列段⽤於後續的構造拼接。

來看⼀個例⼦,假設程式中存在以下⼀段程式碼⽚段:

此時這⼀段正常的按順序執⾏的三個指令是不存在 ret 指令的。但是,如果稍加偏移⼀下解釋程式碼的地址時,就會導致出現完全不⼀樣的指令,如下圖所示:

如果按照紅框中的順序解釋這些指令的時候,那麼將會產⽣⾮程式碼預期的結果,會出現原始碼中未出現過的 ret 指令以及 call 指令,這些指令序列被稱為 gadget 。

透過仔細構造這些由ret指令終⽌的指令集,攻擊者可以執⾏原程式中⾮預期的任意惡意程式碼,這種攻擊被稱為 ROP 攻擊。

 

0x02 CET - Shadow Stack

Intel 提出了⼀種基於硬體的 CET 解決⽅案,其中之⼀的 shadow stack 機制⽤於緩解 ROP 攻擊。前⽂可以得知 ROP 依賴於 ret 指令,其中要執⾏的後續指令地址從堆疊中獲得。因此 ROP 攻擊的前提是攻擊者能夠在堆疊中構造資料。那麼再來看 shadow stack 機制是怎麼⼯作的。

 

CET 使作業系統能夠建立⼀個 shadow stack (影⼦棧)。正常情況下,當執⾏ call 指令時,會將 call 指令後⼀條指令地址壓棧。當啟⽤了 shadow stack 後,會同時在普通資料棧和 shadow stack 中壓⼊返回地址,隨後在執⾏ ret 返回時,會將 shadow stack 中的返回地址和普通資料棧中的返回地址做對⽐,如匹配,則正常執⾏,如不匹配,則觸發#CP(Controlflow Protection) 異常。如下圖所示:

ssp 為 shadow stack 的棧頂指標暫存器,和 sp ⼀樣。按圖就是在執⾏ ret 指令時,會同時在兩個棧 pop 返回地址,隨後對⽐值是否相同。

也就是說,在攻擊者擁有篡改堆疊資料的能⼒,利⽤ ROP 技術將返回地址篡改為構造的 gadget 時, shadowstack 機制能夠完全緩解這種情況,因為執⾏ ret 指令時, pop 出的兩個返回地址並不相同。

 

實際效果

使⽤如下 demo ⽤於測試:

看除錯時的情況,此時返回地址已經被篡改:

再看 shadow stack 中的情況,返回地址為預期的 0x4005ff :

繼續執⾏,會發現觸發異常導致崩潰:

直接執⾏時觸發的 #CP 異常:

實際應⽤上,也是可以證明⽬前來說 CET 中的 shadow stack 機制是能夠有效緩解 ROP 攻擊的。

 

0x03 JOP/COP

JOP/COP 攻擊⼿法與 ROP 類似,只不過是把 ROP 中以 ret 指令做跳板的關鍵點替換成了 call/jmp 指令。還是拿講述 ROP章節的兩幅圖舉例,第⼆幅圖中偏移解釋位元組碼後出現了不同的指令,包含了:

這⼀指令序列,這就是 COP 中的 gadget ,以 call 指令為跳板,也就不需要 ret 指令的輔助了。

 

這種不需要 ret 指令的攻擊場景下,前⾯所說的 shadow stack 機制就失效了。這種情況下, CET 的第⼆種機制 IBT(Indirect Branch Tracking) 就應運⽽⽣了。

 

0x04 CET - IBT

IBT 的設計原理是透過編譯器在合理的間接跳轉( call/jmp )中⽤新的指令做標記,新指令包含 endbr32 和endbr64 。

舉例來說,有如下圖所示的⼀個間接跳轉,編譯器給 main和 foo 都新增了 endbr64 標記。

在執⾏間接跳轉 call 時,如果 IBT 機制啟⽤, CPU 會判斷下⼀跳指令是否為 endbr32/64 ,若是,則正常執⾏,若不是則觸發 #CP 異常。

 

繼續上圖,當 call rdx 按正常流程⾛時,後⼀條指令就是 foo 函式的 endbr64 指令,程式會正常執⾏,如果此時攻擊者篡改了 rdx 的值,將其指向 foo 中的 add rax, rbx 指令地址,則後續執⾏時 CPU 發現指令不為 endbr64 ,會觸發 #CP 異常。

當然了,為了相容以往的架構,啟⽤了 IBT 的程式在不⽀持 IBT 的 CPU 上運⾏時也能夠正常運⾏,這種情況下 endbr32/64 指令會被視為 NOP 指令。

 

那麼 CPU 是如何實現這種校驗機制的呢?答案是⽤了 ENDBRANCH 狀態機。

CPU 在⽤戶態和核心態分別設有⼀個 ENDBRANCH 狀態機,狀態機共有兩個狀態,為 IDLE 和WAIT_FOR_ENDBRANCH 。初始狀態都為 IDLE ,當執⾏間接跳轉 call 的時候,狀態機會從 IDLE變為WAIT_FOR_ENDBRANCH ,這時候就意味著下⼀條指令就必須為 endbr32/64 ,如果是 endbr32/64 ,狀態機就會從 WAIT_FOR_ENDBRANCH 變回 IDLE ,如果不是則會觸發 #CP 異常。

實際效果

使⽤如下 demo ⽤於測試:

再來看看 gdb 中的除錯情況,當再次執⾏ stru1.ops 時,也就是間接跳轉 call rax 時, ops 已經被篡改了:

篡改的地址為 shell 函式中的⼀⾏指令,並不為 endbr64 :

執⾏到篡改地址後繼續執⾏,觸發崩潰:

直接執⾏時觸發的 #CP 異常:

同樣地,實際應⽤上也是可以證明⽬前來說 CET 中的 IBT 機制是能夠有效緩解 COP/JOP 攻擊的。

 

0x05 總結

以上就是 CET 的概述了。總的來說 CET 在硬體層⾯實現的緩解機制與以往的軟體層⾯緩解機制有著⽐較⼤的不同,在緩解效果上⾯增強了許多。但是本質上 CET 是在最底層( CPU 硬體層⾯)做了改動,那麼上層也仍然需要配合底層作出相應的改動:作業系統需要使能 CET 以及配合 CET 做檢測,編譯器需要給應⽤程式做使能 CET 標記...這些都是需要在以往軟體、作業系統、編譯器層⾯做較⼤改動的,同時還肩負著相容性的問題。

那麼在這種多層⾯的複雜場景下,能否找到繞過 CET 緩解機制的⽅法呢,讓我們敬請期待吧。

 

0x06 引用

  • https://www.intel.com/content/www/us/en/developer/articles/technical/technical-look-control-flow-enforcement-technology.html

  • https://firmianay.gitbooks.io/ctf-all-in-one/content/doc/4.5_defense_rop.html

  • https://defuse.ca/online-x86-assembler.htm#disassembly2

  • https://binpwn.com/papers/control-flow-enforcement-technology-preview.pdf

  • https://publishedprd.lanyonevents.com/published/rsaus18/sessionsFiles/8831/HTA-F01_Enhance%20Virtualization%20Stack%20with%20Intel%20CET%20and%20MPX.pdf

  • https://lpc.events/event/2/contributions/147/attachments/72/83/CET-LPC-2018.pdf

  • https://www.intel.com/content/dam/develop/external/us/en/documents/catc17-introduction-intel-cet-844137.pdf

 


相關文章