通過洩漏KVA SHADOW映射破壞Windows KASLR(上)
Windows中的PML4隨機分配如前所述,PML4表是64位內存模型中的最高分頁級別,在Windows中,僅通過使用最低的0x100條目(256)來映射用戶內存,而使用最高的0x100條目來映射內核內存,此表用於分隔用戶和內核內存。知道每個PML4條目都可以映射512GB(0x8000000000位元組),並且為用戶模式分配了0x100條目,我們可以通過執行以下計算來得到用戶虛擬地址範圍:
user address range: 0 ~ 0x100 * 0x8000000000
也就是:
user address range: 0 ~ 0x00007FFF'FFFFFFFF (0x8000'00000000 - 1)
由於Windows用於分頁管理的一種特殊技術稱為「自引用(self-referential)」,它由指向自身的PML4條目(指向包含該條目本身的PML4表)組成,因此整個分頁表的虛擬地址可以是僅通過知道PML4表的地址即可計算得出。同時,只要知道啟動後Windows內核將哪個PML4條目用作「自引用」,就可以計算PML4的地址。
下面是一個PML4屏幕截圖,可以看到一個自引用條目:
最初,此表始終分配給相同的內核地址,因為用於執行此操作的條目位於固定表位置(位於0x1ED條目中)。只需執行以下計算就可以計算原始Windows PML4地址:
0xFFFF0000'00000000 + ( 0x8000000000 + 0x40000000 + 0x200000 + 0x1000 ) * 0x1ED = 0xFFFFF6FB7DBED000.
在所有Windows版本中都使用地址0xFFFFF6FB7DBED000,直到將其隨機化為止。
Windows中的PML4隨機化Windows分頁表隨機化是在2016年發布的「Windows 10」 v1607(RS1-「周年更新」)中引入的。由於它仍使用自引用輸入技術,因此隨機化僅限於PML4表中的256個位置,這意味著只能在256個不同的內核地址中分配該表,這是一個非常糟糕的隨機化。如前一節所述,通過知道哪個PML4條目是自引用的,可以計算PML4表地址。
如果我們想知道所有PML4可能的地址,可以執行以下操作:
for ( entry = 0x100 ; entry < 0x200 ; entry ++ )
{
pml4_address = 0xFFFF000000000000 + ( 0x8000000000 + 0x40000000 + 0x200000 + 0x1000 ) * entry;
printf ( 「PML4 address: %llx\n」 , pml4_address );
}
由於此表是隨機的,並且沒有Windows API可以告訴我們該表的位置,因此內核漏洞對頁面調度表的濫用正在減少。
實際利用場景中的PML4內核漏洞利用和濫用分頁表不是很流行,這很可能是由於需要對分頁系統有很好的了解。確實,使用內存內容映射其他內存並不是一個簡單的概念。
另一方面,可以使用對分頁表的修改來構建原語,比如任意內核讀/寫,甚至內核代碼執行。此外,它們甚至可以被運行在任何完整性級別的內核利用所執行,包括低IL。這使得它們成為一種非常強大的方式,以逃避最難的沙箱實現,如Chrome渲染程序。
下面列出了一些基於濫用分頁表的技術:
雙重寫入:通過將用戶PDE和用戶PTE都指向相同的物理地址來覆蓋它們,這使我們可以從用戶模式控制整個PAGE TABLE,可以將其用作讀/寫原語!
大頁(Huge Page) :通過啟用「PS」位並創建一個巨大的頁面來覆蓋用戶PDPTE,這使我們能夠從PDPT條目中設置的物理地址開始讀取/寫入1GB的連續物理內存。
需要說明的是,這個「大頁」功能已經在cpu中存在了一段時間,但是我們仍然可以找到不支持它的計算機。
大內存分頁(LargePage):通過啟用「PS」位並創建一個「大內存分頁」來覆蓋用戶PDE,這使我們有機會從PDPT條目中設置的物理地址開始讀取/寫入2MB的連續物理內存。
儘管它不如「大頁」選項強大,但在某些情況下,當我們掌握一些有關要映射的物理內存內容的信息時,它可能會很有用,例如使用NULL物理地址(PFN number 0)並讀取或寫入HAL堆的內容。
目標頁:通過設置任意物理地址覆蓋用戶PTE。這是最不常見的情況,但是如果你確切知道要讀取或寫入的數據映射到哪個物理地址,則它是最簡單的選擇。
自引條目:在四個分頁級別中的一些中創建一個自引用條目。如果我們知道一個用戶分頁表的物理地址,則可以覆蓋指向自身的條目,該條目通過從用戶模式操作該表來提供讀/寫原語,就像上面描述的「雙寫」技術一樣。
SMEP繞過:通過將用戶代碼轉換為內核代碼來禁用「 U」位來覆蓋用戶PTE。
根據上面提到的技術,可能只需要打開一些位就可以創建有效的分頁表條目,這意味著它對於內核利用程序產生的大多數「寫在哪裡」條件確實很有用。
Meltdown漏洞被修復後Windows中的PML4情況現在有必要指出,Windows Meltdown漏洞修復程序存在一個「設計」漏洞,其中並非所有內核敏感數據都在用戶模式下隱藏。如前所述,使用兩種不同的PML4來緩解Meltdown漏洞攻擊,即在用戶模式下運行時,CPU使用shadowPML4。
這樣漏洞就發生了,Shadow PML4映射到Shadow內核內存中,這意味著即使實施了Meltdown漏洞緩解措施,它也暴露在用戶模式下。這樣,作為自引用輸入技術的結果,可以通過使用Meltdown漏洞攻擊洩漏Shadow PML4的所有映射的分頁表!
需要明確的是,儘管正確設置PML4對於有效的虛擬內存實現至關重要,但確實沒有必要將其映射到虛擬內存中,或者至少不是永久地映射它,而只是在需要時進行映射。顯然,在某些情況下,當用戶內存通過用戶代碼映射時,Windows內核必須刷新shadow和完整的PML4。
刷新是在內核模式下完成的,在內核模式下使用的是完整的PML4,而不是shadow模式。因此,由於Shadow PML4隻是4KB的內存塊,因此可以像其他內存分配一樣將此表映射到內核空間的任何部分,而無需將其暴露在用戶模式下。
話雖如此,但在shadowPML4中映射分頁表的原因並不清楚,除了shadow到全模式(反之亦然)之間的上下文切換之間的性能漏洞。在下面的截圖中,我們可以看到Meltdown攻擊是如何將PML4的第一部分轉儲到最新的「Windows 10」版本(20H1)中,這與虛擬地址範圍0~0x380 ' 00000000 (0~7GB)有關:
當然,為了能夠洩漏上圖中所示的數據,必須知道分頁表的分配位置。
從客戶端發送的任何形式的密碼,無論是明文、散列還是加密的,都應該是密碼本身。當然,密碼是加密的。但是,知道這個值並將其發送給伺服器,就可以向應用程式驗證該用戶的身份。僅模糊參數值和不安全加密整個傳輸不會提供額外的防禦。通過wireshark觀察到的另一個值是登錄過程的輸出參數GUID。在應用程式的經過身份驗證的部分中,接受GUID作為參數。
這就是我們在業務中稱為「會話ID」的內容。僅使用此值,只要會話保持活動狀態,攻擊者就可以以博客用戶的身份執行經過身份驗證的操作。
但即便如此,事情也變得比實際需要的更加複雜。在這個示例中,我從該登錄過程中捕獲了博客的用戶名和加密密碼。
現在,像Echo Mirage這樣的工具就派上用場了。Echo Mirage允許攔截和修改TCP通信。我可以使用用戶名「blogger」和任意密碼對應用程式進行身份驗證,然後攔截包含「登錄」過程的數據包。
如果胖客戶端基於三層體系結構,則測試的網絡部分與測試Web應用程式基本相同。首要是代理流量。在「Burp」的「代理」選項卡中,在127.0.0.1上設置偵聽器並選擇埠。
可能有很多流量不屬於被測試的應用程式,所以為了確定哪些請求來自胖客戶端,就只有在知道應用程式可以訪問的完整主機集之後,限制Burp的範圍。通過BetaFast,我確定應用程式只向http://www.betafast.net:8080發送請求。
現在,Burp可以像在任何web應用程式測試中一樣使用。下面,我向Repeater選項卡發送了一個請求,並找到了SQL注入。
通過Meltdown漏洞進行PML4去隨機化在上述Windows隨機化後的PML4部分中,我們可以看到PML4隨機性很差,只能接受256個不同的地址。此外,看一看前面的部分,我們可以看到使用Meltdown攻擊可以從分頁表中洩漏數據。
最後,自引用條目使用的權限標誌由值0x63 (Dirty、已訪問、可寫和Present)表示,它到PML4的偏移量由PML4本身的地址決定。因此,使用Meltdown查找在正確的偏移量處將該字節設置為0x63的條目允許查找PML4。綜合所有這些,我們可以推斷出僅通過執行以下代碼就可以知道PML4表的分配位置:
for ( entry = 0x100 ; entry < 0x200 ; entry ++ )
{
pml4_address = 0xFFFF000000000000 + ( 0x8000000000 + 0x40000000 + 0x200000 + 0x1000 ) * entry;
// Kernel address used to leak a single byte
if ( leak_byte_via_meltdown ( pml4_address + entry * 0x8 ) == 0x63 )
{
// PML4 found!
return ( pml4_address );
}
}
在下面的屏幕截圖中,我們可以看到通過使用Meltdown漏洞在大約16毫秒的時間內如何對該表地址進行隨機化。
可以在此處下載該PML4洩漏程序。
PML4去隨機化上述的非隨機化破壞了Microsoft隨機化分頁表的工作,使它們可以再次被預測。由於無法通過調用任何Windows API來獲取PML4地址,因此該技術對於任何以低或中完整性級別運行的內核特權升級漏洞確實有用。
值得一提的是,在Meltdown漏洞出現之前,Enrique Nissim (@kiqueNissim)於2016年在Ekoparty的「我知道你的頁面在哪裡:去隨機化Windows 10內核」演講(視頻)中展示了另一種PML4去隨機化技術。
通過Meltdown漏洞洩漏NT隨著Meltdown漏洞修復程序的到來,出現了一組新的NT函數。添加這些函數是為了在啟用此緩解時能夠處理系統調用和用戶異常,所有這些新函數的名稱都與原始函數類似,除了在名稱中添加陰影后綴之外。例如,除零異常函數最初命名為KiDivideErrorFault,現在是KiDivideErrorFaultShadow。
這些Shadow函數只是原始函數的包裝,完成了從Shadow到完整分頁表的上下文切換。在下面的屏幕截圖中,我們可以看到設置CR3寄存器時如何加載完整的分頁表:
所有這些代碼都放在名為KVASCODE的文件部分中,該部分位於ntoskrnl.exe模塊中,該部分的大小為0x3000位元組(3頁)。由於系統調用和異常必須存在以保持作業系統工作,因此有必要將其映射到shadow端,這意味著它們必須暴露在用戶模式下。如果可以通過使用Meltdown讀取分頁表條目,那麼有可能會洩漏shadow代碼映射的位置。
通過Meltdown漏洞洩漏NT如上所述,Intel 64位內存模型使用四個分頁表級別,其中通常使用最低級別(PAGE TABLES)來映射虛擬內存。為了能夠檢測到shadow代碼位於何處,必須檢測正在使用哪些PTE映射此代碼段。由於這是可執行部分,因此相應的PTE禁用了XD位,這使識別shadow代碼變得更加容易。
因此,使用Meltdown漏洞(使我們能夠讀取分頁表的內容),可以通過從Shadow PML4開始處理四個分頁級別來找到此代碼,並在出現有效條目時傳遞到下一個較低的級別。如果重複此過程並找到三個連續的可執行PTE,則表示已找到shadow代碼。使用Meltdown漏洞,只需執行以下步驟即可:
for ( pml4e = 0x100 ; pml4e < 0x200 ; pml4e ++ ) // Starting from 0x100 because user/kernel division
for ( pdpte = 0 ; pdpte < 0x200 ; pdpte ++ )
for ( pde = 0 ; pde < 0x200 ; pde ++ )
for ( pte = 0 ; pte < 0x200 ; pte ++ )
if ( is_three_consecutive_executable_PTEs ( pte ) == TRUE )
Shadow NT code found!
找到此段代碼後,最後一步是從KVASCODE段減去「ntoskrnl.exe」基地址的增量偏移,如下所示:
NT base = KVASCODE_address - KVASCODE_delta_from_NT
由於此Shadow代碼是NT的一部分,因此獲取基地址只是一個簡單的減法。重要的是要弄清楚此部分偏移量(delta)在Windows發行版之間會發生變化,但是對於同一發行版的不同「ntoskrnl.exe」版本來說,通常是相同的。對於Windows 20H1「 2020年5月更新」, NT基本地址為0xa21000位元組,與0xa21頁相同。
在以下屏幕截圖中,我們可以看到如何在900毫秒內獲得「ntoskrnl.exe」基地址:
可從此處下載此NT基址洩漏程序。
注意:從「Windows 10」 RS6開始,在RAM內存等於或大於4GB的計算機上運行時,ntoskrnl.exe的基地址對齊為2MB。
在這種情況下,KVASCODE部分的檢測速度要快得多,因為頁表中的偏移量是固定的,這大大減少了洩漏進程到1/512的次數,並且通過只洩漏一個可執行PTE的內容,使這個過程更容易。
通過Meltdown漏洞洩漏NT洩漏的基地址NT內核使用的是一種很常見的技術升級利用特權,它用於定位系統和當前EPROCESS PsInitialSystemProcess鍊表結構,然後定位標記的結構,用於獲得系統權限。
當以中等完整性級別運行時,獲取NT基址是很簡單的,只需調用NtQuerySystemInformation函數就可以獲得它。在運行於較低完整性級別(通常是沙箱)的進程中,需要有信息洩露漏洞來獲得前面所述的NT基地址。在大多數情況下,信息洩露漏洞對於擺脫受限進程(例如Chrome,Edge或Firefox等瀏覽器使用的沙箱)絕對是必不可少的。
總結儘管Meltdown漏洞是一個非常著名的攻擊漏洞,並且所有在Intel CPU下運行的作業系統都受到了影響,但是沒有充分的證據表明它在野外使用。更糟的是,除了一些概念證明之外,使用Meltdown漏洞進行Windows攻擊的公共內核攻擊並不多。該文章證明了即使啟用了Meltdown漏洞緩解功能,該攻擊仍然有效,它可以用於實際的攻擊,以破壞Windows KASLR,然後可以將其與內核特權提升漏洞結合使用,從任何沙盒應用程式中逃脫。
參考及來源:https://labs.bluefrostsecurity.de/blog/2020/06/30/meltdown-reloaded-breaking-windows-kaslr/