CVE-2020-1362 漏洞分析

酷酷的曉得哥發表於2020-07-28

作者:bybye@知道創宇404實驗室
時間:2020年7月24日

原文地址:

漏洞背景

WalletService 服務是 windows 上用來持有錢包客戶端所使用的物件的一個服務,只存在 windows 10 中。

CVE-2020-1362 是 WalletService 在處理 CustomProperty 物件的過程中出現了越界讀寫,此漏洞可以導致攻擊者獲得管理員許可權,漏洞評級為高危。

微軟在 2020 年 7 月更新對漏洞釋出補丁。

環境搭建

  1. 復現環境:windows 10 專業版 1909 (內部版本號 18363.815)
  2. 設定 WalletService 服務啟動型別為自動

  3. 除錯環境:windbg -psn WalletService 即可。

漏洞原理與分析

漏洞點是設定 CustomProperty 物件的 Group 的 get 方法和 set 方法沒有檢查邊界。

  1. get 方法的 a2 引數沒有檢查邊界導致可以洩露堆上的一些地址。

  2. 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 // 介面類例項指標的地址);
  1. 首先,我們需要 WalletXFactory 的 CLSID,可以使用 OLEViewDotNet 這個工具檢視。

  2. 其次,我們需要一個 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\::`vector deleting destructor(),因為這個函式呼叫的 LoadLibraryExW() 會使用一個全域性變數作為想要載入的 dll 的路徑。

我們可以透過上面的 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]
[2] 微軟更新公告
[3]


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912109/viewspace-2707389/,如需轉載,請註明出處,否則將追究法律責任。

相關文章