一、背景介紹
5月中旬ESET披露了其捕獲的PDF文檔樣本中的兩枚0-day漏洞,其中包含針對Windows系統的內核提權漏洞。該漏洞的漏洞編號為CVE-2018-8120,Windows已經提供安全更新修復此安全漏洞。天融信阿爾法實驗室將以Windows Server 2003 32位系統為目標,詳細介紹該漏洞成因、如何觸發漏洞、以及如何使用該漏洞制作“本地應用程序權限提升”工具。
經驗證,諸多版本的Windows系統均存在該漏洞,文末同時附帶一份利用該漏洞制作的提權工具,以供學習。經測試該工具支持Win2003 x32/x64、WinXP x32、Win7 x32/x64, Win2008 x32/64。

1.1 漏洞描述
部分版本Windows系統win32k.sys組件的NtUserSetImeInfoEx()系統服務函數內部未驗證內核對象中的空指針對象,普通應用程序可利用該空指針漏洞以內核權限執行任意代碼。
1.2 受影響的系統版本
以下軟件版本受到影響。未列出的版本要么超過其支持生命周期,要么不受影響。要確定軟件版本或版本的支持生命周期,請查閱Microsoft支持生命周期。
Windows 7 for 32-bit Systems Service Pack 1
Windows 7 for x64-based Systems Service Pack 1
Windows Server 2008 for 32-bit Systems Service Pack 2
Windows Server 2008 for 32-bit Systems Service Pack 2
Windows Server 2008 for Itanium-Based Systems ServicePack 2
Windows Server 2008 for x64-based Systems Service Pack 2
Windows Server 2008 for x64-based Systems Service Pack 2
Windows Server 2008 R2 for Itanium-Based Systems ServicePack 1
Windows Server 2008 R2 for x64-based Systems ServicePack 1
Windows Server 2008 R2 for x64-based Systems ServicePack 1
1.3 漏洞編號
CVE-2018-8120
二、漏洞細節
2.1漏洞位置及形成原因
漏洞函數位于win32k.sys模塊的SetImeInfoEx() 函數, 該函數在使用一個內核對象的字段之前并沒有進行是否為空的判斷,當該值為空時,函數直接讀取零地址內存。如果在當前進程環境中沒有映射零頁面,該函數將觸發頁面錯誤異常,導致系統藍屏發生。
以下是漏洞產生位置的反匯編代碼

可以看到,漏洞所在函數SetImeInfoEx()接收2個參數,漏洞的產生和參數1的結構體指針有關,下面跟蹤一下參數1的來源。win32k!NtUserSetImeInfoEx() 系統服務函數調用了SetImeInfoEx()

_GetProcessWindowStation()返回當前進程的WindowStation內核對象, 當做參數1調用SetImeInfoEx()。以下是WindowStation內核對象的內存結構

程序可以通過系統提供的接口CreateWindowStation()和SetProcessWindowStation(),新建一個新的WindowStation對象并和當前進程關聯起來,值得注意的是,使用CreateWindowStation() 新建的WindowStation對象其偏移0×14位置的spklList字段的值默認是零。
根據SetImeInfoEx()函數的流程,當WindowStation->spklList字段為0,函數繼續執行將觸發0地址訪問異常。
2.2漏洞觸發驗證
前文已經介紹了漏洞所在位置,下面編寫漏洞測試代碼,測試該漏洞是否能夠觸發系統藍屏。

NtUserSetImeInfoEx()系統服務函數未導出,需要自己在用戶進程中調用該系統服務函數,以執行漏洞函數SetImeInfoEx()。

其中SyscallIndex的計算,根據系統ShadowSSDT表導出序號計算。

編譯生成poc,開始執行


系統藍屏,可以發現錯誤產生位置為0xBF91B399,下面在IDA中查看對應地址的指令,正是前文指出的SetImeInfoEx()中針對pWindowStation->spklList字段進行內存訪問的代碼。

三、漏洞利用
3.1漏洞利用之任意代碼執行
由于SetImeInfoEx()沒有正確的處理內存中的空指針對象, 普通應用程序可利用該漏洞以系統權限執行任意代碼,下面將詳細介紹如何在該漏洞現場實現任意代碼執行。
[1] [2] 下一頁已知漏洞產生的原因是零地址內存訪問違例,如果在漏洞函數運行的進程中,零地址處的內存分頁完成映射,則函數將繼續執行。下面繼續看看函數如果繼續運行,會發生什么情況。

如上圖所示,漏洞產生函數后續執行過程中會執行內存拷貝,且拷貝源來自于參數2,屬于用戶可控內容。如果拷貝目標v4可控,則可以實現任意內存地址寫入(且漏洞函數運行在內核權限, 內核空間與用戶空間內存均有權限讀寫)。至此,如果可以實現任意內存地址寫入,則可以通過覆蓋系統服務函數指針的方式, 實現任意代碼執行。
現在的目標是使得拷貝目標v4可控,已知v4來自spklList+0x2C,當spklList為零,而零地址分頁內存又正好被應用程序映射,一來SetImeInfoEx()可以繼續執行不觸發異常,二來也是v4是可控的。
通過內核未導出函數可以在部分Windows 系統上成功映射零地址分頁的內存,函數原型所示

映射零地址分頁內存后,可以在該地址上進行內核對象的構造,以滿足SetImeInfoEx()函數的檢查, 通過漏洞函數的memcpy操作覆蓋關鍵系統服務函數指針。 改造漏洞驗證代碼,嘗試覆蓋ntoskrnl!HalDispatchTable表中第二項的hal!HaliQuerySystemInformation() 函數指針,NtQueryIntervalProfile()函數在運行過程中會從HalDispatchTable表中調用該函數。使得用戶程序在調用系統函數NtQueryIntervalProfile()的時候,執行由應用程序設定的ShellCode。


最終驗證該代碼不能成功覆蓋預設的函數指針,原因很簡單。目標地址0x8088e07c,無法通過漏洞函數的第二個判斷,位置如下圖所示

首先直接這樣觸發漏洞覆蓋該函數指針表0x15C字節大小,造成影響較大,二來由于漏洞函數本身的限制,挑選合適的覆蓋位置也比較困難, 下面簡要介紹一種流行的方法,用來協助我們進行精準的覆蓋。
利用Bitmap內核對象中的pvScan0字段,進行的任意內存地址讀寫。通過系統API SetBitMap()和GetBitMap()可以實現指定地址寫入/讀取功能。該方法已有文章進行詳細的分析,下面附一篇文章鏈接讀者可以去查閱,這里不再贅述。
https://bbs.pediy.com/thread-225436.htm
下面修改漏洞驗證代碼,首先利用漏洞覆蓋預先創建的BitMap內核對象, 接著再使用BitMap內核對象進行精準覆蓋。實現函數指針覆蓋,以下是加入使用BitMap進行精準覆蓋的代碼。


成功的覆蓋指定的函數指針,應用程序接著調用該系統服務函數接口,操作系統就會執行我們設定的Shellcode,下面是一段使用SYSTEM進程 EPROCESS結構的Token替換當前進程Token的代碼,執行過該代碼后,本進程具有和SYSTEM進程一樣的權限。

在替換當前進程EPROCESS結構中Token標識后,當前進程后續創建的進程均為SYSTEM權限的進程,成功進行權限提升獲得SYSTEM權限。
3.2 漏洞利用實例-提權工具
本文演示之漏洞利用代碼取自GitHub 感謝作者@unamer。 該作者開源的代碼包含了一份針對Win7及Win 2008系統的提權工具。
筆者根據該代碼做了部分修改,增添了針對Windows 2003 sp2(x32 x64)及Windows XP sp2 x32系統的提權支持。主要是漏洞利用代碼中自行調用了系統內核服務函數,不同系統其系統服務調用號并不一致。同時還修改了部分結構體偏移,以支持Win2003和WinXP。在WinXP x64系統上, 由于NtUserSetImeInfoEx() 中對win32k!gpsi結構中標志位進行驗證而未能執行含有漏洞的代碼,故未能成功利用該漏洞。
修改后的漏洞利用工具源碼及Bin下載地址如下:
https://github.com/alpha1ab/CVE-2018-8120
四、修復建議
目前微軟在2018年五月的安全更新中已經包含了針對該漏洞的補丁程序,安裝使用即可避免受到該漏洞的影響。
漏洞修復程序下載地址:
https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-8120
|