作者:bybye@知道創宇404實驗室
時間:2020年7月24日
漏洞背景
WalletService 服務是 windows 上用來持有錢包客戶端所使用的對象的一個服務,只存在 windows 10 中。
CVE-2020-1362 是 WalletService 在處理 CustomProperty 對象的過程中出現了越界讀寫,此漏洞可以導致攻擊者獲得管理員權限,漏洞評級為高危。
微軟在 2020 年 7 月更新對漏洞發布補丁。
環境搭建
- 復現環境:windows 10 專業版 1909 (內部版本號 18363.815)
-
設置 WalletService 服務啟動類型為自動

-
調試環境:windbg -psn WalletService 即可。
漏洞原理與分析
漏洞點是設置 CustomProperty 對象的 Group 的 get 方法和 set 方法沒有檢查邊界。
-
get 方法的 a2 參數沒有檢查邊界導致可以泄露堆上的一些地址。

-
set 方法的 a2 參數沒有檢查邊界,可以覆蓋到對象的虛表指針,從而控制程序流。

漏洞利用過程
創建 CustomProperty 對象
WalletService 服務由 WalletService.dll 提供,WalletService.dll 實際上是一個動態鏈接庫形式的 Com 組件,由 svchost.exe 加載。我們可以在自己寫的程序(下面稱為客戶端)中使用 CoCreateInstance() 或者 CoGetClassObject() 等函數來創建對象,通過調用獲得的對象的類方法來使用服務提供的功能。
如何創建出漏洞函數對應的對象呢?最簡單的辦法是下載 msdn 的符號表,然后看函數名。
我們想要創建出 CustomProperty 對象,ida 搜索一下,發現有兩個創建該對象的函數:Wallet::WalletItem::CreateCustomProperty() 和 Wallet::WalletXItem::CreateCustomProperty()。

所以我們創建一個 CustomProperty 需要一個 WalletXItem 對象或者 WalletItem 對象,那么使用哪個呢?繼續用 ida 搜索 CreateWalletItem 或者 CreateWalletXItem,會發現只有 CreateWalletItem。

那到這里我們需要一個 WalletX 對象,繼續用 ida 搜索會發現找不到 CreateWalletX,但是如果搜索 WalletX,會發現有個 WalletXFactory::CreateInstance(),如果有過 Com 組件開發經驗的同學就會知道,這個是個工廠類創建接口類的函數,上面提到的 CoCreateInstance() 函數會使 WalletService 調用這個函數來創建出接口類返回給客戶端。

那么如何調用 WalletXFactory::CreateInstance() 并創建出 WalletX 對象呢?我們需要在客戶端使用 CoCreateInstance() 。
HRESULT CoCreateInstance(
REFCLSID rclsid, // CLSID,用于找到工廠類
LPUNKNOWN pUnkOuter, // 設置為 NULL 即可
DWORD dwClsContext, // 設置為 CLSCTX_LOCAL_SERVER,一個宏
REFIID riid, // IID, 提供給工程類,用于創建接口類實例
LPVOID *ppv // 接口類實例指針的地址
);
-
首先,我們需要 WalletXFactory 的 CLSID,可以使用 OLEViewDotNet 這個工具查看。

-
其次,我們需要一個 WalletX 的 IID,這個可以用 ida 直接看 WalletXFactory::CreateInstance() 這個函數。

有了 WalletXFactory 的 CLSID 和 WalletX 的 IID,然后在客戶端調用 CoCreateInstance(),WalletService 就會調用 CLSID 對應的工廠類 WalletXFactory 的 CreateInstance(), 創建出 IID 對應的 WalletX 對象,并返回對象給客戶端。
然后按照上面的分析,使用 WalletX::CreateWalletItem() 創建出 WalletItem 對象,然后使用 WalletItem::CreateCustomProperty() 創建出 CustomProperty 對象。
對于上面的步驟有疑問的同學可以去學一學 Com 組件開發,尤其是進程外組件開發。
偽造虛表,覆蓋附表指針
由于同一個動態庫,在不同的進程,它的加載基址也是一樣的,我們可以知道所有dll里面的函數的地址,所以可以獲得偽造的虛表里面的函數地址。
那么把虛表放哪里呢?直接想到的是放堆上。
但如果我們繼續分析,會發現,CustomProperty 類里面有一個 string 對象,并且可以使用 CustomProperty::SetLabel() 對 string 類進行修改,所以,我們可以通過修改 string 類里面的 beg 指針 和 end 指針,然后調用 CustomProperty::SetLabel() 做到任意地址寫。

有了任意地址寫,我們選擇把虛表放在 WalletService.dll 的 .data 節區,以避免放在堆上可能破壞堆上的數據導致程序崩潰。
控制程序流到 LoadLibrary 函數
使用偽造 vtable 并覆蓋虛表指針的辦法,我們可以通過調用虛函數控制 WalletService 的程序流到任意地址了。
那么怎么提權呢?在 windows 服務提權中,通常的辦法是把程序流控制到可以執行 LoadLibrary() 等函數來加載一個由我們自己編寫的動態鏈接庫,因為在加載 dll 的時候會執行 dll 里面的 DllMain(),這個方法是最強大的也是最實用的。
這里使用漏洞提交者的方法,把虛表的某個地址覆蓋成 dxgi.dll 里面的 ATL::CComObject\

我們可以通過上面的 SetLabel() 進行任意地址寫,修改上圖的全局變量 Src,使其指向我們自己實現的動態鏈接庫的路徑,然后調用對應的虛表函數,使程序流執行到 LoadLibrarExW() 即可。
實現一個動態鏈接庫
在 DllMain() 里面寫上我們希望以高權限執行代碼,然后調用虛表里面對應的函數是 WalletService 的程序流運行到 LoadLibraryEx() 即可。
注意,因為 windows 服務運行在后臺,所以需要在 DllMain() 里面使用命名管道或者 socket 等技術來進行回顯或者交互,其次由于執行的是 LoadLibraryExW(),所以這里的 dll 路徑要使用寬字符。
其它
在控制虛表函數程序流到 LoadLibraryExW() 時,需要繞過下面兩個 check。
第一個是需要設置 this+0x80 這個地址的值,使得下面的 and 操作為 true。

第二個是要調整 qword_C5E88 和 qword_C5E80 是下面的變量 v4 指向具有寫權限的內存。

漏洞利用結果
可以獲得管理員權限

補丁前后對比
可以看到,打了補丁之后,get 方法和 set 方法都對 a2 參數添加了邊界檢測。


參考鏈接
[1] PoC鏈接
[2] 微軟更新公告
[3] nvd漏洞評級
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1276/
暫無評論