從DirectX到Windows核心——幾個CVE漏洞淺析
一、前言
作業系統核心是每個漏洞利用鏈的最終目標,大家可以檢視Zero Day Initiative (ZDI) Pwn2Own歷年比賽,瞭解這方面內容。Windows核心一直以來都是攻擊者熱衷的目標,我最喜歡的就是濫用 DeviceIoControl
呼叫來與各種驅動打交道,這樣就能訪問許多廠商編寫的各種驅動,其中許多驅動程式碼寫得並不完善,也沒有經過完備測試。
多年以來,許多攻擊者都藉助 win32k.sys
來攻擊Windows核心,這是一個核心模式裝置驅動,可以控制Windows圖形及視窗管理系統。當微軟將該功能從CSRSS中遷移到核心時,進入Windows核心的攻擊面也增加了1倍或者3倍,從那時起這已經成為許多漏洞的發源地。
在過去十年期間,自從WDDM(Windows Display Driver Model)取代早期的XDDM後,大家又找到了另一個巨大的攻擊面。顯示系統呼叫操作首先會經過 win32k.sys
處理,但在此之後,使用者程序就可以直接呼叫 dgxkrnl.sys
,或者通過 GDIPlus
中的入口點直接呼叫其他驅動。這進一步擴大了攻擊面,因此引起了研究人員的濃厚興趣。
2018年春季,ZDI從騰訊ZhanluLab的ChenNan及RanchoIce手中購買了5個針對DirectX核心介面的漏洞,利用這些漏洞成功從微軟獲取了4個CVE編號。本文分析了這些漏洞,並且提供了相應的PoC程式碼(程式碼已在我們網站上公佈)。
此外,Rancho和ChenNan在9月份的44CON會議上介紹過其中一種攻擊技術(ZDI-18-946/CVE-2018-8405),強烈建議大家去學習此次演講的 簡報 。
二、DirectX概覽
在分析漏洞之前,我們首先來簡要回顧一下DirectX介面及驅動。
DirectX圖形核心子系統由3個核心模式驅動所組成: dxgkrnl.sys
、 dxgmms1.sys
以及 dxgmms2.sys
。這些驅動會通過 win32k.sys
以及自己的介面來與使用者通訊。此外,這些驅動也會與 BasicRender.sys
、 BasicDisplay.sys
以及miniport(微型埠)顯示驅動通訊。
DirectX定義了許多複雜的核心物件,大部分物件名以 DXG
開頭。使用者通過許多複雜的API介面與DirectX互動,其中許多介面以 D3DKMT
開頭,其他介面以 DXGK
開頭。
其中比較有趣的部分入口點如下所示:
D3DKMTEscape D3DKMTRender D3DKMTCreateAllocation
從攻擊角度來看,來自IOActive的Ilja van Sprundel曾在2014年的Black Hat會議上做過關於WDDM的一次演講,題目為“ Windows Kernel Graphics Driver Attack Surface ”,這是非常好的概述資料。強烈推薦大家先參考這份材料,其中詳細介紹了有關WDDM核心方面的複雜攻擊面。
三、漏洞分析
大家可以訪問 此處 下載PoC原始碼。如果大家想復現崩潰問題,需要安裝2018年8月份之前的Windows版本(當時Windows還沒打上補丁)。在測試過程中,記得將核心偵錯程式attach目標主機上,並在待攻擊的驅動上設定Special Pool(特殊池)。我已在Windows 10 x64位系統上測試過本文分析的這些漏洞。
ZDI-18-946/CVE-2018-8405:D3DKMTCreateAllocation型別混淆漏洞
我們分析的第一個漏洞位於 dgxkrnl.sys
的 DXGDEVICE::CreateAllocation
方法中,可通過 D3DKMTCreateAllocation
介面觸發,本地攻擊者可以利用該漏洞將許可權提升到 SYSTEM
級別。大家可以訪問 此處 閱讀我們的安全公告,訪問 此處 獲取微軟補丁。漏洞根源在於驅動沒有正確驗證使用者提供的資料,導致存在型別混淆情況。
為了復現漏洞,我們需要在執行PoC之前在 dxgkrnl.sys
上設定一個Special Pool。型別混淆問題源自於在pool分配中沒有正確使用 CrossAdapter
標誌。在pool分配過程中,PoC程式碼將 CrossAdapter
標誌設定為 0
,然後將所得控制代碼傳遞給第2個分配過程,其中 CrossAdapter
標誌被設定為 1
。
藍屏資訊分析如下:
錯誤程式碼位於 DXGDEVICE::CreateAllocation
,這是一個在分配過程結束時的一個典型的型別混淆問題:
ZDI-18-947/CVE-2018-8406:D3DKMTRender型別混淆漏洞
下一個漏洞位於 dxgmms2.sys
驅動中,可通過 D3DKMTRender
方法觸發。攻擊者同樣可以利用這個漏洞將許可權提升到 SYSTEM
級別。大家可以訪問 此處 瞭解我們的安全公告,訪問 此處 獲取相應補丁。與第一個漏洞一樣,這個bug會導致出現型別混淆情況。雖然本質上相似,但這些bug的根本原因並不相同。
同樣,我們需要在 dxgkrnl.sys
和 dxgmms2.sys
上啟用Special Pool才能復現bug,當然我們也需要將核心偵錯程式attach到目標主機。這個型別混淆源自於兩個不同介面卡之間混亂的分配操作。
相關PoC程式碼如下:
PoC崩潰細節:
存在漏洞程式碼如下:
ZDI-18-950/CVE-2018-8400:D3DKMTRender不可信指標引用解析漏洞
這個漏洞同樣可以由 D3DKMTRender
例程觸發。漏洞位於 dxgkrnl.sys
的 DGXCONTEXT::ResizeUserModeBuffers
方法中。大家可以訪問 此處 瞭解我們的安全公告,訪問 此處 獲取微軟補丁。由於驅動在將使用者提供的值作為指標解析引用(dereference)時,並沒有正確驗證這個值,因此導致這個bug出現。出現指標dereference問題,是因為驅動會信任使用者設定的一個標誌。相關PoC細節如下:
導致出現崩潰現象:
呼叫棧:
存在漏洞的程式碼:
顯然,使用者提供的標誌本不應該導致核心中出現任意dereference問題。
ZDI-18-951/CVE-2018-8401:BasicRender競爭條件漏洞
最後一個漏洞稍微有點複雜,漏洞位於 BasicRender
驅動對 D3DKMTMarkDeviceAsError
API以及 D3DKMTSubmitCommand
API的處理過程中。大家可以訪問 此處 閱讀我們的安全公告,訪問 此處 下載微軟補丁。這個漏洞中,共享資源並沒有得到適當的保護,可能導致出現記憶體破壞問題。攻擊者可以利用這個漏洞將許可權提升為 SYSTEM
級別。惡意軟體經常使用這類許可權提升方法,在使用者不小心點選某些東西的時候將自己安裝到目標系統中。需要注意的是,微軟為這個bug和 ZDI-18-949 分配了同一個CVE編號,表明這兩個漏洞的根本原因相同。
這兩個漏洞的PoC程式碼存在相關性,但有所區別。
第一個PoC的關鍵程式碼如下:
每次呼叫 SubmitCommand
時都會通過 VidSchiWorkerThread
生成一個執行緒。呼叫 MakeDeviceError
會修改相同物件的狀態,導致出現競爭條件。
最終會出現崩潰:
對同一個位置有兩次修改,出現競爭條件:
對於 ZDI-18-949
,雖然漏洞根源一樣,但我們還是可以在PoC程式碼中看到不同之處。PoC中關鍵程式碼如下:
執行這個PoC會導致 Run
方法崩潰:
存在漏洞的程式碼如下:
存在漏洞的程式碼會在第二次執行 Run
時崩潰。
四、總結
WDDM以及DirectX圖形核心程式碼使用了許多複雜物件、為使用者程式碼建立許多新的複雜介面,從而為Windows提供了非常強大和靈活的圖形系統。分析前文提供的PoC後,大家應該對DirectX在物件實現上的複雜度以及未來該領域可以研究的方向有所瞭解,我認為該領域還有許多尚未挖掘的財富。
通過直接靜態分析方法,我們還是可以獲取一些攻擊資訊,然而這肯定是一項艱鉅的任務。還有一種可能採取的方法,我們可以部署一個模糊測試框架,在不同的標誌上設定不同的值,然後以不同的順序來呼叫DirectX方法,查詢崩潰點。當然,我們也可以新增多個執行緒修改及釋放資料,來尋找是否存在競爭條件和TOC/TOU問題。另外別忘了在所有相關驅動上設定Special Pool。
老生常談,Zero Day Initiative對新漏洞非常感興趣,當大家發現新漏洞時,可以通過推特( @FritzSands )聯絡我,也可以關注我們團隊的 推特 獲取最新漏洞利用技術和安全補丁資訊。