Jump to content

robbery.png

雖然數據執行保護(DEP)旨在阻止來自特定內存區域的純代碼注入攻擊,但道高一尺魔高一丈,攻擊者已經不再注入整個代碼的有效負載了,而是重新利用DEP允許的內存頁面中的多個代碼塊,稱為ROPgadget。這些代碼塊取自目標應用程序中現有的代碼,並鏈接在一起,以形成所需的攻擊者有效負載,或僅按頁禁用DEP,以允許運行現有代碼有效負載。

為了永久阻止ROP攻擊,Intel開發了一種新的硬件強制控制流完整性緩解措施,稱為控制強制技術(CET),大約兩年前首次在Windows系統上發布。

我們會在本文中簡要介紹CFI緩解措施的工作原理,包括CET,以及如何利用Counterfeit Object-Oriented Programming (COOP) 在最新Windows版本上有效繞過Intel CET。

Forward-Edge和Backward-EdgeCFI控制流完整性機制可以分為兩大類:Forward-Edge和Backward-Edge。

與Microsoft CFG4一樣,Forward-EdgeCFI通過使用經過驗證的函數地址來保護間接函數調用。例如,如果我們用ROPgadget地址重寫CALL [rax]指令中解引用的指針,CFG將通過發出異常來阻止我們的攻擊。

相反,像Intel的CET5這樣的Backward-EdgeCFI通過將函數的返回地址與存儲在Shadow Stack上的以前保存的地址版本進行比較來保護函數的返回地址。如果原始返回地址在內存被攻擊期間被重寫了,則地址對照( address comparison)將不可避免地失敗,應用程序將被終止。考慮到基於ROP的攻擊在沒有“CALL”指令的情況下執行“RET”指令,運行線程的堆棧和影子堆棧(shadow stack )值不匹配,因此像CET這樣的Backward-EdgeCFI有效地阻止了這種攻擊技術。

Intel CET旨在通過間接分支跟踪(IBT)通過影子堆棧和COP/JOP緩解ROP攻擊。然而,由於後一種技術尚未在Windows上實現,因此在本文中,我們將把“Intel CET”作為僅啟用影子堆棧的實現。

CET當前的實現方式非常無效,因為渲染器進程通常會被利用。儘管CET在瀏覽器上還沒有得到廣泛的執行,但我們應該期待它在未來的每一個進程中都得到執行。

偽造對象Felix Schuster在2015年提出了一種名為偽面向對象編程(COOP)的新代碼重用技術。不過該技術尚未在野外或公開利用被發現。我們寫這篇博文的目的是試圖利用這種理論方法,並在概念驗證中加以實現,以繞過Intel CET。

該技術背後的主要思想是偽造,即用攻擊者控制的有效負載在內存中生成新的對象,並通過目標應用程序或加載庫中已經存在的虛擬函數將它們鏈接在一起。

偽對像中包含的每個虛擬函數稱為vfgadget,負責執行一個小任務。與ROP類似,vfgadget可以執行諸如將值填充到寄存器中之類的任務。然而,當組合在一起時,多個vfgadget可以執行更高級的操作。

由於目前沒有專門的工具可以發現vfgadget,所以可以通過自定義腳本(如idpython)找到它們,使用類似於ROP gadget發現的過程。

由於vfgadget是從CFG有效函數池中選取的,我們可以將它們標記為合法的,一旦我們劫持了其中一個函數的間接調用,它們的執行就不會被CFG阻止。

此外,一個有趣的推論是Intel CET不會被觸發,因為我們不會在順序調用vfgadget的過程中損壞任何函數返回地址。

一個典型的COOP有效載荷從一個充當COOP主要功能的基本vfgadget開始。我們在本文稱之為Looper。一旦攻擊者在內存中集成了偽造對象,Looper vfgadget就會遍歷由攻擊者精心安排的其他vfgadget數組,這些vfgadget將被逐個調用。通過以這種方式對齊偽對像中的vfgadget,我們將能夠以可控的方式調用有效的虛擬函數。

Looper運行後,它可以調用其他負責執行特定操作的vfgadget,如Argument Loaders Invoker和Collector。這些vfgadget將定期存儲在Looper訪問的數組中。

Argument Loader vfgadget通過將值加載到給定寄存器中來填充該寄存器。要加載的值將存儲在偽對像中,位於假對像開始處的偏移位置。

一旦寄存器被一個或多個參數加載器填充,就可以調用Invoker vfgadget來簡單地執行目標API的函數指針。

Collector是一種gadget,用於檢索寄存器中已存在的值,並將其保存回攻擊者的偽造對象即作為從調用的API返回的值。

下圖總結了迄今為止討論的COOP攻擊策略。

1.png

COOP攻擊流

我們可以根據不同的vfgadget的可用性和想要執行的API來安排和混合它們。

為了更好地理解COOP攻擊,讓我們從分析主vfgadget Looper開始。以下彙編代碼提供了Looper COOP vfgadget的簡化版本:

2.png

Looper Gadget相關的ASM代碼

在第一行中,RCX持有this9指針,我們將偽對象的開頭加載到RBX中,該偽對象位於RCX的偏移量0x40處。由於偽對像中的所有項目都將以偏離此指針的偏移量為準,因此我們需要確保在劫持程序流之前保存其值(即通過破壞vtable)。

然後,COOP有效負載基址被解引用到RAX中,RAX指向被調用的第一個vfgadget。一旦調用返回,一個新的vfgadget將從前一個gadget的偏移量0x20處加載,如果RBX的內容不為零,則會進行新的循環迭代。

當在內存中寫入偽造對象時,我們需要預先對齊每個vfgadget以匹配Looper偏移量,類似於下面的佈局:

3.png

內存中的COOP緩衝區

這樣,00000227`26cd8900是COOP有效負載的基址,它存儲在‘this’指針(RCX)的偏移量0x40處。從前面的代碼清單中,我們注意到在_loopstart例程的第一行,指針被解引用到RAX中,而RAX又指向第一個vfgadget。在下一次循環迭代中,Looper通過在前一個循環的偏移量0x20處加載指針來重複相同的任務,並最終調用第二個vfgadget。

當利用真實的目標瀏覽器時,建議依賴Looper vfgadget,因為它比其他vfgadget提供了更多的控制和穩定性。但是,為了簡潔起見,我們在編寫易受攻擊的應用程序時只使用了一個Invoker vfgadget,它只接受一個參數。

在介紹了COOP的基本理論之後,讓我們繼續開發一個由CET編譯的概念驗證應用程序,該應用程序是我們為展示COOP攻擊而開發的。

與COOP繞過CET影子堆棧我們編寫的易受攻擊的應用程序是用CET和CFG以及默認啟用的DEP編譯的。

首先,為了驗證CET是否真的被強制執行,我們在printf上放置一個斷點,檢查調用堆棧,覆蓋返回地址並恢復執行。

4.png

驗證CET的執行

當收到一個涉及無效返回地址的Shadow Stack異常提示,就代表CET被啟用了。

由於CET是一種硬件強制緩解,為了觸發上述漏洞,我們至少需要一個英特爾虎湖( Tiger Lake )十一代酷睿CPU。

為了模擬瀏覽器漏洞,我們可以利用執行應用程序時自動觸發的應用程序中的類型混淆漏洞來獲得RIP控制。

當我們點擊該漏洞的觸發器時,vtable指針會被我們的輸入損壞,導致我們可以控制的間接調用。然後我們劫持vtable,使其指向第一個(也是唯一一個)vfgadget所在的COOP緩衝區。如上所述,為了簡潔起見,我們沒有使用帶有嵌套vfgadget的Looper,而是選擇使用一個同時具有Invoker和Argument Loader組件的gadget。

作為該漏洞自動利用的一部分,為了獲取vfgadget的‘this’指針,我們洩漏了堆棧指針,並將‘this’指針作為堆棧的靜態偏移量進行檢索。

一旦我們獲得了‘this’指針地址,我們就準備好要調用的WindowsAPI的地址及其參數。這是通過在偽對象內以所需偏移量寫入Windows API地址和參數來實現的。

在更詳細地研究COOP有效負載之前,讓我們先通過不帶任何參數運行PoC來理解它的語法。

5.png

獲取PoC的語法

應用程序接受四個參數:一個指向偽造對象(COOP)緩衝區的指針、vfgadget地址、Windows API地址及其參數。該助手顯示了兩個簡單的用例,但可以將其擴展為調用任何CFG允許的API(如果應用程序是用它編譯的)。

由於Windows DLL將在隨機基址加載,因此需要預先計算所需的API地址。

讓我們首先檢查與vfgadget相關的對象的C++代碼,然後從編譯的二進製文件探索其相應的程序集:

6.png

我們從中派生vfgadget的' trigger '方法

項目中的OffSec類包含一個觸發器方法,它充當一個C風格的函數指針,我們可以使用它來調用任何我們喜歡的API。然後,在主程序例程中實例化“OffSec”類,以便將其與方法一起加載到內存中。

仔細查看反彙編程序中的Invoker可以發現一些有趣的方面。

7.png

調用程序vfgadget

從第二行到第四行,RCX引用的‘this’指針首先存儲在堆棧中,然後移動到RAX中。接下來,從RAX偏移量0x10處的值被解引用並移動到RAX中。此值將是駐留在偽對像中的API函數指針。然後,在第7行和第8行,第一個函數參數從‘this’指針偏移量0x8處解引用,並移到RCX中。

我們很快就會發現,一旦我們從命令行提交了參數,易受攻擊的應用程序就會處理這些偏移。

在介紹了攻擊鏈的主要構建塊之後,讓我們嘗試通過傳遞四個參數來運行PoC,以獲得代碼執行:

8.png

運行帶有所有參數的PoC應用程序

通過上述命令,我們提供了以下參數:00001e000000作為偽對象的存儲緩衝區,5086014001000000作為Invoker vfgadget,40610fecfb7f0000是WinExec內存地址。作為最後一個參數,我們傳遞WinExec字符串參數。請注意,所有內存地址都是以little-endian格式傳遞的。

一旦啟動,應用程序立即停止,允許我們將調試器附加到它。在這樣做之前,我們啟動Process Explorer以驗證二進製文件實際上在啟用Intel CET的情況下運行。

9.png

驗證是否已使用Process Explorer啟用CET

在“堆棧保護”欄下,Process Explorer確認僅對CET兼容模塊強制執行CET,這意味著將對使用CET編譯的任何模塊強制執行緩解。附加調試器後,我們將斷點放置到主函數中唯一的間接調用,然後繼續執行。

10.png

間接調用時中斷

我們在main+0x3d2處放置了一個斷點,並驗證了在該地址確實有一個間接調用。接下來,我們將轉儲位於靜態地址0x1e0000的偽造對象的內容,該地址包含指向位於0000000 1400186a0的vfgadget的指針。

在main+0x3d2是類型混淆錯誤引發的地方,允許我們控制RIP。一旦到達斷點,我們就檢查駐留在COOP緩衝區中的值,它應該是第一個Invoker vfgadget。我們讓應用程序繼續運行,並驗證我們確實達到了斷點。

11.png

登錄第一個COOP vfgadget

在跟踪到CFG ldrpdispatchusercalltarget例程之後,我們跳轉到Invoker vfgadget ' OffSec:trigger ',證明我們已經控制了程序的執行流。然後我們繼續在vfgadget中進行跟踪:

12.png

將‘this’ 指針移動到RAX中

在上面的清單中,Invoker首先將‘this’指針從RCX保存到堆棧中,我們還驗證它是否指向COOP緩衝區的底部。在最後一條指令中,‘this’ 指針被加載到RAX中,RAX將用作調用API及其參數的引用:

13.png

通過‘this’ 指針加載WinExec參數

首先,在偏移量0x10處,我們可以看到WinExec地址被加載到RAX中,然後,在三條指令之後,命令參數被檢索到偏移量0x8處。

如果執行繼續,我們將再次調用LdrpDispatchUserCallTarget,它依次將執行分派給WinExec。

14.png

成功調用WinExec

這就完成了我們的簡單概念證明,我們介紹了通過調用CFG允許的函數,同時避免損壞任何返回地址,可以繞過Intel CET Shadow Stack並通過COOP攻擊獲得任意代碼執行。

此PoC應用程序的Visual Studio項目可以在這個URL中找到。

總結Intel CET提供了另一種強大的防禦機制,儘管如此,攻擊者可以採用新的攻擊途徑,如COOP來繞過這種緩解措施。

0 Comments

Recommended Comments

There are no comments to display.

Guest
Add a comment...