一、前言
操作系統內核是每個漏洞利用鏈的最終目標,大家可以查看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
:這個入口點以用戶完全可控的一段數據作為輸入。輸入數據可能非常大,因此系統很有可能將其存儲在用戶內存中,而不會在切換到內核處理期間在內核中捕獲這段數據。這樣一來,如果沒有妥善處理,相關內核例程就很容易存在TOC/TOU( time of check,time of use,基于檢驗時間/使用時間的一種異步攻擊)漏洞。這段數據并不是標準化結構,每個驅動都有自己的定義。D3DKMTRender
:這個入口點是實際渲染圖形數據的核心。來自用戶地址的命令以及patch緩沖區會交由內核驅動來解釋,實際上這些數據會傳遞給miniport驅動。同樣,這也是競爭條件問題的滋生地。此外,渲染過程還會生成worker線程,更容易出現競爭條件漏洞。D3DKMTCreateAllocation
:這個入口點用來分配內存。由于傳遞給API的不同標志和句柄之間有各種復雜的相互作用,因此可能會出現一些問題(參考下文的ZDI-18-946內容)。從攻擊角度來看,來自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位系統上測試過本文分析的這些漏洞。
我們分析的第一個漏洞位于dgxkrnl.sys
的DXGDEVICE::CreateAllocation
方法中,可通過D3DKMTCreateAllocation
接口觸發,本地攻擊者可以利用該漏洞將權限提升到SYSTEM
級別。大家可以訪問此處閱讀我們的安全公告,訪問此處獲取微軟補丁。漏洞根源在于驅動沒有正確驗證用戶提供的數據,導致存在類型混淆情況。
為了復現漏洞,我們需要在運行PoC之前在dxgkrnl.sys
上設置一個Special Pool。類型混淆問題源自于在pool分配中沒有正確使用CrossAdapter
標志。在pool分配過程中,PoC代碼將CrossAdapter
標志設置為0
,然后將所得句柄傳遞給第2個分配過程,其中CrossAdapter
標志被設置為1
。
藍屏信息分析如下:
錯誤代碼位于DXGDEVICE::CreateAllocation
,這是一個在分配過程結束時的一個典型的類型混淆問題:
下一個漏洞位于dxgmms2.sys
驅動中,可通過D3DKMTRender
方法觸發。攻擊者同樣可以利用這個漏洞將權限提升到SYSTEM
級別。大家可以訪問此處了解我們的安全公告,訪問此處獲取相應補丁。與第一個漏洞一樣,這個bug會導致出現類型混淆情況。雖然本質上相似,但這些bug的根本原因并不相同。
同樣,我們需要在dxgkrnl.sys
和dxgmms2.sys
上啟用Special Pool才能復現bug,當然我們也需要將內核調試器attach到目標主機。這個類型混淆源自于兩個不同適配器之間混亂的分配操作。
相關PoC代碼如下:
PoC崩潰細節:
存在漏洞代碼如下:
這個漏洞同樣可以由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)聯系我,也可以關注我們團隊的推特獲取最新漏洞利用技術和安全補丁信息。