作者:洪禎皓@螞蟻安全實驗室
原文鏈接:https://mp.weixin.qq.com/s/TMe_Qxh4CZB3L0GiA1hbqA
近日,螞蟻安全光年實驗室洪禎皓的議題《Mobius Band Explore Hyper-V Attack Interface through Vulnerabilities Internals》入選全球最頂級的極客大會Black Hat USA 2021,該議題重點分享了微軟Azure云的虛擬化解決方案Hyper-V的新型攻擊面。
虛擬化軟件作為業務和硬件的連接橋梁,一旦被發現漏洞,很可能橫向影響多臺虛擬機,縱向影響業務軟件及物理機安全。Hyper-V作為微軟虛擬化解決方案的代表,微軟對其安全漏洞的獎金高達25萬美金,可見微軟對其安全性的重視與信心,業內相關的研究資料也是鳳毛麟角。
本篇議題首先介紹了Hyper-V的基本架構,以及Guest中的數據是如何傳輸到Host中的Hyper-V組件并加以解析的。然后通過和Windows平臺上EOP漏洞利用的比較,總結出Hyper-V在安全研究中存在的難點。議題中還公開了三個已經修復的Hyper-V RCE漏洞,這三個漏洞都可以在Guest中觸發,并可導致Hyper-V Host執行任意代碼。通過探究這些漏洞的成因和觸發方式,讀者將了解到Hyper-V的多個攻擊面。
01 Hyper-V架構介紹
Hyper-V總體架構如下:

Hyper-V的Hypervisor架構如下:

Hyper-V的VMBUS架構如下:

Hyper-V中的Host Driver架構如下:

Hyper-V的Ring3架構如下:

02 Hyper-V Guest和Host之間如何通信
Hyper-V Guest和Host之間的通信依靠VMBUS組件。在Guest中,vmbus_open函數用來初始化Guest中的VMBUS Channel。vmbus_open函數的流程如下:

vmbus_sendpacket函數被Guest用來向Host發送數據,vmbus_on_event函數被Guest用來接收Host發來的數據。
下面介紹在Host中的VMBUS,這里有兩個重要的函數:vmbkmclr!KmclpVmbusManualIsr和vmbkmclr!KmclpVmbusIsr。
vmbkmclr!KmclpVmbusManualIsr用來分發Guest數據到Host驅動,vmbkmclr!KmclpVmbusIsr用來分發數據到Host用戶態組件。
下面是Guest數據到Host驅動的路徑,這里拿storvsp驅動為例。

下面是Guest數據到Host用戶態組件的路徑,這里拿vmiccore組件為例。

03 Hyper-V安全研究的難點
這里我們用Win32k!EngRealizeBrush整數溢出漏洞(MS17-017)比較Hyper-V在安全研究中的難點。
在攻擊面層面上,傳統的EoP漏洞可以使用的系統API很多,并且Ring0中的驅動可以直接從用戶態地址讀取數據。而Hyper-V中,沒有可以使用的系統API,所有的數據都通過VMBUS傳輸,Ring0中的驅動無法直接從Guest地址中讀取數據。
在內核對象的分配和釋放層面上,傳統的EoP漏洞有很多內核對象可以利用、很容易控制內核對象的分配和釋放、通過利用內核對象構造讀寫原語也相對容易。在Hyper-V中,目前還在尋找適合用作漏洞利用的內核對象、對內核對象的釋放與分配無法做到直接的控制,也無法做到直接控制分配與釋放的時機、某些內核對象中只有很少的內容可以從Guest中控制。
在TOCTTOU類型的漏洞層面上,傳統的EoP需要滿足兩個基本點,第一是內核模塊取到一個用戶態的指針,第二是從這個用戶態指針中獲取數據超過兩次。但在Hyper-V中,所有的數據都通過VMBUS機制傳輸,Ring0組件無法直接通過Guest中的地址讀取到數據。
04 漏洞細節
這個漏洞編號為CVE-2019-0620,下面是觸發這個漏洞的棧回溯。

發生這個漏洞的根源在調用vmbkmclr!VmbChannelPacketComplete函數時,兩次調用中使用了相同的第一參數。

我們在WinDbg中下斷點,并且進行調試。
斷點內容為:bp storvsp!VstorCompleteScsiRequest+0x2d7 "r @rcx;k;r @$thread;!pool @rcx;.echo ; g"。


下面的棧回溯在正常流程中被觸發:

下面的棧回溯只有在vhdmp!VhdmpiPerformExtraScsiActions函數的第二參數偏移0x08處的內存不為NULL時被觸發。
使用vhdmp!VhdmpiScsiCommandWriteUsingToken和vhdmp! VhdmpiScsiCommandCopyOperationAbort函數可以間接的將vhdmp!VhdmpiPerformExtraScsiActions函數的第二參數偏移0x08處的內存寫入一個指針,并觸發如下棧回溯。

vhdmp!VhdmpiScsiCommandWriteUsingToken和vhdmp! VhdmpiScsiCommandCopyOperationAbort函數的上層調用是vhdmp!VhdmpiScsiCommandCopyOperations。

如圖所示,v5可以通過Guest控制。

POC代碼節選如下:

下個漏洞的編號為CVE-2019-0720。下面是崩潰時的現場。

RtlDeleteElementGenericTableAvl函數的第二參數是RtlLookupElementGenericTableAvl函數的返回值。


RtlDeleteElementGenericTableAvl函數從generic table中刪除一個特定的元素并且隨后釋放掉這個特定元素。vmbusr!ChUnmapGpadlView函數的第二參數是gpadl_handle,gpadl_handle可以被Guest控制。
vmbusr!ChUnmapGpadlView函數運行在多線程的環境中,也就是多CPU環境中。vmbusr!ChUnmapGpadlView函數的第二參數也就是gpadl_handle可以被Guest控制,并且該函數第一參數可以被我們使用的channel控制。
假設有如下的情況:
1、有兩個線程ThreadA和ThreadB,運行在不同的CPU上。
2、ThreadA和ThreadB同時運行到vmbusr!ChUnmapGpadlView函數。
3、這兩個線程調用vmbusr!ChUnmapGpadlView函數使用同樣的參數。
4、ThreadA比ThreadB要快一點點。
我們把vmbusr!ChUnmapGpadlView函數分為三個狀態。如下圖:

第一步,ThreadA首先會獲取到自旋鎖,ThreadA處于狀態1并持有鎖。同時,ThreadB也會試圖獲取并等待鎖。
第二步,ThreadA調用函數RtlLookupElementGenericTableAvl并返回指針A,然后ThreadA釋放掉自旋鎖。
第三步,ThreadB會獲取到相同的鎖,ThreadA現在在試圖獲取并等待鎖。這個時候,ThreadA在狀態2,ThreadB在狀態1并且持有鎖。
第四步,ThreadB調用RtlLookupElementGenericTableAvl函數并且返回指針B,然后ThreadB釋放掉自旋鎖。因為兩個線程調用vmbusr!ChUnmapGpadlView函數使用了相同的參數,所以指針A和指針B是相同的。
第五步,ThreadA會獲取到第二個自旋鎖,然后調用vmbusr!ChDeleteGpadlViewIfUnreferenced函數并且釋放掉指針A指向的內存,并且將指針A從generic table中刪除。最后,ThreadA會釋放掉自旋鎖。現在,ThreadB在狀態2,ThreadA在狀態3。
第六步,ThreadB獲取到第二個自旋鎖,然后調用函數vmbusr!ChDeleteGpadlViewIfUnreferenced釋放到指針B指向的內存,并且將指針B從generic table中刪除。
因為指針A和指針B相等,所以vmbusr!ChDeleteGpadlViewIfUnreferenced函數使用了一個已經釋放掉的內存作為第二參數,然后,發生UAF。
所以,如果我們想觸發這個問題,必須要有兩個必要條件:1、有兩個互不干擾的線程運行到vmbusr!ChUnmapGpadlView函數。2、兩個線程調用vmbusr!ChUnmapGpadlView函數擁有相同的參數,即gpadl_handle相同。
幸運的是,我找到了兩個不同的線程調用vmbusr!ChUnmapGpadlView函數,并且可以控制它們變成兩個互不干擾的線程。下圖是這兩個線程的棧回溯。


Thread1可以通過向宿主機發送NVSP_MSG1_TYPE_REVOKE_RECV_BUF消息和CHANNELMSG_GPADL_TEARDOWN消息觸發。代碼節選如下:

Thread2可以通過模擬按下虛擬機的reset鍵觸發。代碼節選如下:

并且,efi.rest_system會觸發一個重要的線程去控制Thread1和Thread2變成兩個互不干擾的線程。下面是這個重要線程的棧回溯:

這個重要的線程需要在Thread1和Thread2前運行,所以我們需要使用下面的代碼來設置vmswitch!VmsVmNicPvtRevokeRecieveBufferWorkItem函數進入睡眠狀態。

POC代碼的原理是設置vmswitch!VmsVmNicPvtRevokeRecieveBufferWorkItem函數的第二參數偏移0xe0處的內存設置成非0,并且vmswitch!VmsVmNicPvtRevokeRecieveBufferWorkItem函數會進入睡眠狀態直到第二參數偏移0xe0處的內存設置成0。幸運的是,我們也可以調用efi.rest_system把第二參數偏移0xe0處的內存設置成0。

現在,我們可以創建兩個互不相干的調用vmbusr!ChUnmapGpadlView函數的線程了。
下面是這個漏洞的觸發和調試過程。






最后一個漏洞是CVE-2020-16891,下面是這個漏洞崩潰時的現場。


這個漏洞觸發需要使用windows server系列的操作系統,而且需要如下設置。在網卡的選擇上,我使用了Intel(R) Ethernet 10G 4P X540/I350 rNDC #2網卡。

這個問題發生在vmwp!EmulatorVp::FlushGvaTranslationCache函數中,這個函數如下圖所示。


在偏移0x9E處,vmwp!EmulatorVp::FlushGvaTranslationCache函數會調用vmwp! VND_HANDLER_CONTEXT::RemoveReference函數。在vmwp! VND_HANDLER_CONTEXT::RemoveReference函數中會調用vmwp!Vml::VmSharableObject::DecrementUserCount函數,如果函數vmwp!VND_HANDLER_CONTEXT::RemoveReference第一個參數偏移-0x50處的值為1的話,vmwp!Vml::VmSharableObject::DecrementUserCount將會釋放掉一個VmbComMmioHandlerAdapter對象,這個對象的大小為0xb0。
函數vmwp!VND_HANDLER_CONTEXT::RemoveReference第一個參數偏移-0x50處的值為一個引用計數器的地址,如果這個引用計數器的值為1,一個VmbComMmioHandlerAdapter對象就會被釋放。在下面的文章中,我將這個引用計數器稱為KEY_REF_COUNTER。
我們發現,調用vmwp!VndCompletionHandler::HandleVndCallback函數也可以減少上文中提到的KEY_REF_COUNTER。在vmwp!VndCompletionHandler::HandleVndCallback函數偏移0xAAE位置處,調用了Vml::VmSharableObject::DecrementUserCount函數。如圖。

我們可以通過構造下面的POC代碼,控制vmwp.exe進程運行到vmwp!VndCompletionHandler::HandleVndCallback函數偏移0xAAE位置處。


下面的POC代碼用來控制vmwp.exe進程運行到vmwp!EmulatorVp::FlushGvaTranslationCache+0x9e位置處。

但是,這個virt_addr+0x1004地址代表著什么呢?經過查閱Linux Kernel代碼,我發現這個地址可能代表著PCI_COMMAND的地址,向其中寫入2和0分別代表著開啟和關閉內存空間響應這個功能。
下面我們觸發并且調試這個漏洞。下面是斷點信息。

我們直接來到漏洞發生位置,在vmwp!VndCompletionHandler::HandleVndCallback函數偏移0xAAE位置處,KEY_REF_COUNTER被減1,隨后在 vmwp!EmulatorVp::ActuallyAttemptEmulation偏移0x2b3處斷下,通過調試器可以看到,這時的KEY_REF_COUNTER的值為1。如下圖所示。

通過查看VmbComMmioHandlerAdapter對象的地址,發現此時的對象還沒有被釋放,如下圖。

繼續跟蹤,發現VmbComMmioHandlerAdapter對象隨后被釋放掉了。

繼續調試,進程在vmwp!EmulatorVp::ActuallyAttemptEmulation偏移0x290處斷下。此時的rcx寄存器中存放的正是剛剛被釋放的VmbComMmioHandlerAdapter對象的地址,用WinDbg查看這個地址的狀態,如下圖。

05 攻擊面
vmcall調用、虛擬化MSR讀寫、APIC虛擬化、嵌套虛擬化等是Hypervisor組件攻擊面。

Guest可以通過發送CHANNELMSG_OPENCHANNEL等消息到達VMBUS組件的攻擊面。

虛擬設備如vmswitch、storvsp、動態內存等,在Guest中可以通過調用vmbus_sendpacket發送特定的虛擬設備數據到達對應的驅動文件或者dll文件中。如下兩圖。


06 潛在攻擊面
Hyper-V的潛在攻擊面有vmswitch中的Packet Direct功能、Network Direct功能、PCI-E PassThrough、虛擬機層嵌套虛擬化等等。這些組件不是虛擬機默認配置,但是可以通過特定配置打開。換句話說,這些組件功能并不常用,可能是一些比較好的研究方向。
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1705/
暫無評論