當前位置:
首頁 > 新聞 > 竟態攻擊:Hyper-V安全問題分析

竟態攻擊:Hyper-V安全問題分析

前言


硬體虛擬化為廣大從事IT行業的朋友提供了極大的便利。同時也是「雲」這一概念的重要支持技術之一。微軟在前有VM,QEMU等產品的廣泛應用下毅然決然的發布了微軟編寫的虛擬化產品Hyper-V,並運用到了Microsoft Azure 雲計算平台中。今年的 Blackhat 大會中也有議題對 Hyper-V 的安全問題展開了討論。今天我們將對其從安全形度上進行深度的分析。


0x01 工作原理


先上一張Hyper-V結構的布局圖:


當用戶開啟虛擬化(Intel VT)支持後,物理機會在進入Windows 內核前啟動 hvix64.exe/hvax64.exe 初始化 Hypervisor 層再運行 Windows 內核程序完成系統載入。



當 Hypervisor 層被啟用後,宿主機在操作硬體時也會通過 Hyperisor 再傳至物理設備,因此 Hyperisor 層的分級則為 Ring -1 (最新版的 Hyper-V 則會支持部分操作不進入Ring -1層直接傳至物理設備進行操作)


宿主機內核與虛擬機內核都是Ring 0級操作,只不過宿主機可以控制虛擬機,所以,如果虛擬機發生了故障,不會影響到宿主機;宿主機發生了非致命故障,例如並非藍屏等Ring 0下的故障,也不會影響虛擬機的正常運行。


宿主機與虛擬機之間的數據傳輸通過VMBus(虛擬數據匯流排)實現,VMBus是微軟開發的虛擬數據傳輸匯流排,用來宿主機和虛擬機之間交換數據和信息。使用VMBus的優點就是能進行快速、高效、大數據量的數據傳輸,極大的提升了虛擬機的性能。VMBus的原理和QEMU中的VirtIO設備類似,通過共享環形內存,每當要傳輸的數據寫滿整個環形內存時,就把數據發送到宿主機中。可以說,微軟成功的借鑒了VirtIO設備的優點,取其優點用之,才會使得Hyper-V虛擬機性能大幅提升。


和QEMU模擬設備埠讀寫的方法不同的是,Hyper-V全部使用了虛擬設備,而不是模擬硬體埠。Hyper-V不再使用傳統的讀寫模擬硬體埠的方式進行硬體設備的虛擬化,而是使用了虛擬設備作為設備模擬。例如,在虛擬機中網卡,硬碟等設備的驅動都是由微軟提供,如果沒有這些驅動程序,虛擬機則無法使用虛擬設備,就像是QEMU中的VIRTIO設備一樣,需要載入特殊驅動,而不是使用和真實硬體一樣的驅動。


下圖表示了虛擬機如何通過宿主機訪問網路:



其中虛擬機與物理機的數據交互如下圖:


虛擬機與物理直接的數據交互通過環形內存的讀寫來完成。對環形內存的讀寫操作由VMBus來完成


下面我將通過這個例子來闡述虛擬機是如何與物理機進行交互的:


虛擬機用戶通過 foo.exe 發起一個 TCP 請求;



請求傳遞到虛擬機的網卡驅動;


虛擬機網卡驅動會通過 VMBus 將請求封裝成一個數據包傳遞到環形內存中;


在虛擬機寫滿一個環形內存或將數據成功寫入環形內存後;


虛擬機使用Hypercall指令通知宿主機,數據已經寫完,併產生一個VM-Exit事件陷入 Hyperisor 層執行代碼;


這時 Hyperisor 層就會出發註冊在 WIndows 內核中的中斷常式(IDT);


Windows 內核接收到消息後會根據虛擬設備類型調用相對應的函數讀取環形內存的數據,並將數據分發到相對應的虛擬設備驅動;

這樣數據就由虛擬機成功的轉移到物理機,相關的虛擬驅動會繼續解析數據;


由物理機傳入數據至虛擬機道理相同;


更細緻的分析可以參考文章。


0x02 測試


根據 Joe Bialek 和 Nicolas Joly 在大會中所講,對於 Hyper V 的攻擊主要在於對宿主機內核態的攻擊,大部分情況是會造成宿主機的崩潰。



有關用戶態的攻擊通常無法造成實質的效果,一般情況會直接被異常接管,漏洞利用及其複雜。


以下是宿主機內核態組件:



VMSwitch.sys:提供半虛擬化網路

StorVSP.sys:提供半虛擬化存儲


VID.sys:虛擬化設施驅動


WinHVr.sys:內核到hypervisor的介面(hypercall)


VMBusR.sys:VMBUS,負責guest與host的通信


vPCI.sys:半虛擬化PCI


根據看雪ID: ifyou 的介紹,在用戶態中的 vmuidevices.dll 組件也非常容易受到攻擊,這是一個負責圖形顯示的組件,由於圖形顯示的程序編寫及其複雜,所以這裡出問題的概率也會大很多。


在 Blackhats 大會中 Joe Bialek 者闡述了一個通過VMSwitch.sys組件實施逃逸的案例。這個時候我們要詳細的講解一下有關於虛擬機與宿主機的數據交互細節。 在原理中我們講到,宿主機與虛擬機的數據交互通過VMBus控制,但數據並不直接由VMBus進行傳輸,而是緩存到兩者之間的環形內存中,環形內存至少存在兩個 一個請求內存,一個應答內存。


下面是虛擬機與宿主機通過環形內存進行數據交互的過程:



虛擬機發送的數據通過VMBus傳入請求內存。

隨後通過Hypercall的方式通知宿主機提取數據,產生VM-EXIT事件陷入Hyperisor層 當宿主機收到消息後會觸發中斷,隨後讀取請求內存的數據,分發給相關的虛擬驅動程序處理。


數據處理完成後向虛擬機發送處理成功的消息,並將應答給虛擬機的數據,寫入應答內存。 虛擬機讀取應答內存的數據並回應一個消息告訴宿主機,應答數據有有效。


宿主機才會釋放線程進入下一個數據包的處理。 當然,環形內存是會被分割成數個子區。環形內存的描述(GPADL)由虛擬機提供,環形內存中的子區大小及其數量由宿主機自適應。


舉個例子:一個應答環形內存的地址以及大小由虛擬機使用GPADL通知宿主機,宿主機會根據GPADL;里提供的指針索引到應答環形內存的地址,然後根據GPADL中提供的內存大小去調整這一段環形內存中子區的大小並生成適配這一段內存的子區分配表。


Blackhats 大會中 Joe Bialek 利用的漏洞便是針對於數據交互中,更新內存指針與更新和生成這一內存中的子區大小及其數量,並非是原子操作(中間存在時間差),提出了競態攻擊。


攻擊設想:



我們可以控制宿主機接收的數據;


我們傳入的數據可以通過竟態完成越界;


越界之後的數據可以執行可控的攻擊。


作者通過RNDIS control message responses來控制寫入的內容。這是虛擬機要發送的網路數據經過虛擬機內核態中的虛擬網路組件構成的 RNDIS 協議數據。


第二點實際上是要求我們控制數據包的延遲,已達到,應答內存的指針已經更新到新的內存地址,但宿主機上依然保留著舊內存地址的子區分配表,這樣就會存在,子區分配表中所描述的總內存大小與新內存的大小不匹配,這會給我們的越界操作提供越界內存。


首先虛擬機發送GPADl通知內存地址指針,宿主機收到通知後更新內存指針,指到新的內存地址,但依舊保留這舊的子區分配表:



然後宿主機根據GPADL中通知的此段內存的大小分配子區大小及其數量,生成新的子區分配表:



生成子區分配表後,執行更新動作,將適配與原先內存的子區分配表量更新到新的子區分配表:



如圖所示,在更新內存指針之後,宿主機還沒有更新子區分配表,子區內存的總大小是和新內存不適配。這樣就存在越界內存 ,給我們提供了存放攻擊載荷的空間。

針對於 RNNIS 協議的數據,宿主機在處理完虛擬機發送的數據後會將應答給虛擬機的數據封裝成 cmplt 數據包,宿主機處理完數據後發送處理成功的消息,並將 cmplt 包 寫入應答內存,等待虛擬機的回應。如果虛擬機沒有回應,則負責處理該數據的線程會一直處於等待狀態,不會處理下一個數據。


當然,並不是每一個 cmplt 包都會被虛擬機回應,比如畸形的 cmplt 數據包。


所以我們可以通過虛擬機傳遞給宿主機的 RNDIS 協議數據 使宿主機在處理數據後生成畸形的 cmplt 數據包。達到阻塞宿主機數據處理線程,造成延遲。 而後在N個畸形數據包後附加一個有效的並帶有攻擊載荷的cmplt數據包,使其在宿主機更新到新的應答內存過程中,指針已經偏移但子區分配表未更新時,寫入越界內存。



當然,最重要的,是我們在成功越界寫入之後,所造成的效果是否是我們可控的,否則將無法造成有效攻擊。


Jordan Rabet 在大會中做出進一步分析。MDL(表明虛擬內存緩衝區的物理頁面布局)映射到物理內存,而這些MDL會被映射到SystemPTE區域。這個區域里通常存放的是其他的MDL以及內核棧。


毫無疑問,我們將內核棧作為我們攻擊的目標,Win的內核棧一共有7頁,6個頁面作為棧空間,而最後一個頁面在底部作為guard page。


那麼問題來了,我們怎樣將內核棧放到我們可以操控的越界空間中,並且我們怎樣開啟一個線程去執行這一步操作。


先來看一下 SystemPTE 分配內存的流程:



它基於一個可以進行擴展的Bitmap 分配以及檢索內存空間;

每個位代表一個頁面的狀態 位0表示空閑頁,1表示已分配使用 內存分配基元 進行內存分配;


從內存分配基元 的位置開始掃描 Bitmap;


如果需要改變某一個內存空間的狀態,則包裝此空間並改變此空間的位;


內存分配基元 會放在成功分配的空間的尾部;


如果找不到空閑內存,則展開Bitmap 搜索新的可用內存。



下面是它分配頁面的一個示例:





由此發現,我們需要一個可控的內存分配基元幫助我們在 SystemPTE 區域中布局內存。 但其實,回顧環形內存的特性,虛擬機可以任意構造大小和數量不限的MDL(他們存在上限,但是非常高,以至於我們不用考慮大小的問題)。


由於宿主機應答給虛擬機的數據(NVSP_MSG1_TYPE_REVOKE_RECY_BUF和NVSP_MSG1_TYPE_REVOKE_SEND_BUF)是可以被撤回的,當然這是一個bug,當多個撤銷消息被處理時,除了最後一個工作線程,其它的工作線程都會被永久死鎖,但我們依然有方法通過這樣的機制去撤回內存的使用。


因此我們就有了內存分配和釋放的基元來幫助我們操控這個區域。但我們還需要生成新的內核棧,以便我們越界後對其進行操控。所以我們還需要堆棧分配基元。


vmswitch依賴系統工作線程執行非同步任務,這些線程被放在內核維護的線程池中,於是我們可以通過向裡邊添加線程的方式獲取這一內核棧的許可權。只有所有的進程都在忙的時候,我們才可以向裡邊加入新的線程。所以我們要做到:



多次快速的觸發非同步任務。如果產生的任務足夠快,那麼就會有新的線程加入。有幾類vmswitch消息依賴於系統工作線程,例如我們使用的NVSP_MSG2_TYPE _SEND_NDIS_CONFIG。 漏洞發掘者 在實驗的過程中利用這樣的方法創建了5個線程。


如果無法創建更多的線程,說明線程池中已經有了足夠多的線程。這個時候我們就要通過上述的撤回bug,鎖死其他線程,造成一個受限的線程棧噴射。(在shellcode的前面加上大量的slide code(滑板指令),組成一個注入代碼段。然後向系統申請大量內存,並且反覆用注入代碼段來填充。這樣就使得進程的地址空間被大量的注入代碼所佔據。)


這樣我們便可以生成一個靠近應答內存的內核棧:



內核的保護機制,能使任何堆緩衝區溢出到內存區域的末尾。我們需要多次申請內存以最大限度的接觸到 SystemPTE 的末尾,如圖中所示,實驗過程中,可替換的應答數據與內核棧距離已經很接近了。


最後的最後我們需要繞過空間地址隨機化防禦Bypassing KASLR。


這裡用到一個信息泄露的漏洞,造成信息泄露漏洞的結構體是nvsp_message,它在棧上進行分配,它只初始化了前8個位元組,卻返回了sizeof(nvsp_message)大小,因此32位元組未被初始化的棧內存會被發回給guest,造成信息泄露。通過泄露的信息,我們能夠獲得vmswitch的返回地址,進而構造rop鏈。



綜上,整個利用過程便是:



使用infoleak定位vmswitch的返回地址;


使用信息建 ROP 鏈,但由於我們並不知道我們破壞的是那個堆,所以我們要構建一個ROP NOP-sled 這意味著我們要連續執行一串RET 指令;


通過 SystemPTE massaging生成可控內核;


使用競爭條件覆蓋宿主機原 內核線程堆棧與ROP鏈;


在宿主機上執行ROP。


防禦手段:


上述漏洞只出現在Windows Server 2012 R2 中 Win 10並沒有這類漏洞。



虛擬機管理程序強制執行的代碼完整性(HVCI);


攻擊者無法將任意代碼注入主機內核;


內核模式控制流保護(KCFG);


攻擊者無法通過劫持函數指針來實現內核ROP;


這裡作者採用的防禦手段是將內核棧遷移到單獨的區域,實現隔離。


後記


搭建 Windbg 雙機調試時,網路雙機調試失敗,依然是通過串口進行雙機調試(延遲略高,調試了一天。。)


Vmware 14.0 可以載入Windows Server 12 虛擬機鏡像(VM12 不支持),如果採用虛擬機嵌套,內存制少分配8G


測試 Joe Bialek 在大會上演示的逃逸漏洞,需要多次調試,本人在測試中多次崩潰(操控SysPTE內存。。。不要灰心,你總能成功!


*本文作者:Murkfox,本文屬 FreeBuf 原創獎勵計劃,未經許可禁止轉載。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 FreeBuf 的精彩文章:

再談Wannacry,安全狗CEO陳奮道出網路安全新時代三大特徵
分析一個用於傳播Hancitor惡意軟體的Word文檔(第一部分)

TAG:FreeBuf |