作者:HuanGMz@知道創宇404實驗室時間:2022年6月7日
分析一下最近Microsoft Office 相關的 MSDT 漏洞。


文檔:
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/wintt/windows-troubleshooting-toolkit-portal
Windows Troubleshooting Platform (WTP) provides ISVs, OEMs, and administrators the ability to write troubleshooting packs that are used to discover and resolve issues found on the computer
WTP 框架提供了一種自動化 檢測/修復 故障的方式。
WTP 結構:
上圖展示了 WTP 的底層結構:
WTP由兩個進程組成,Process1 是帶UI 的 Troubleshooting Run-time Engine,Process2 用於提供 Windows PowerShell Runtime 環境。
Process2 提供的 PowerShell 運行時環境提供了4條特殊的 PowerShell 命令:Get-DiagInput, Update-DiagReport, Update-DiagRootCause, Write-DiagProgress。
Troubleshooting Pack 運行在Process1 和 Process2 所構建的平台上。
故障排除包 是用戶可編程部分,其本質上是一組 針對特定故障的 檢測/修復腳本。Process1 的Troubleshooting Run-time Engine 從故障排除包中獲取 檢測腳本,並交給Process2 運行。Process2 中特殊的 PowerShell 運行時環境提供了4條專用的命令給故障排除包里的腳本使用。
故障排除包的設計基於三個步驟:檢測問題(troubleshooting)、解決問題(resolution)和驗證解決方案(verification),對應 TS_、RS_、VF_ 三種腳本。
實際上 Process1 就是 msdt.exe ,Process2 則是 sdiagnhost.exe。sdiagnhost.exe 為了給msdt.exe 提供運行腳本的能力,註冊了IScriptedDiagnosticHost com接口,相應的com方法就是:RunScript()。
WTP 還提供了一系列的默認故障排除包,可以在 ms-msdt 協議里通過 -id 參數指定。本次漏洞中所使用的 PCWDiagnostic 就是其中之一,用於程序兼容性的故障排除。


漏洞復現:
該漏洞可通過 doc 或 rtf 文檔的形式觸發,但為了調試方便,我們直接使用 msdt.exe 命令觸發:
在 cmd 中使用上面的命令觸發漏洞(不要直接用powershell)。
漏洞調試:
mspaint.exe 進程創建在 sdiagnhost.exe 下,且 PowerShell Runtime 由c# 實現。儘管 sdiagnhost.exe 本身是一個非託管程序,我們仍然可以使用 dnspy 來進行 .net 調試。
設置好 dnspy 調試所需要的環境變量:
在 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\ 註冊表路徑下創建 sdiagnhost.exe 項,並在該項下創建 Debugger 字符串,值為dnspy 路徑。
使用前面的命令觸發漏洞,然後會看到 dnspy 被調用,但處於未開始調試的狀態。此時需要我們手動點擊上面的"啟動" 來開啟調試。
在 Microsoft.Windows.Diagnosis.SDHost.dll 里的 Microsoft.Windows.Diagnosis.ManagedHost.RunScript() 方法下斷點。該方法實現了 IScriptedDiagnosticHost com接口裡的 RunScript() 方法,用於給 msdt.exe 提供執行檢測腳本所需的 PowerShell 運行時環境。然後重新觸發漏洞,便可在此中斷。
RunScript() 方法一共被觸發了兩次,第一次用於調用 TS 腳本,第二次用於調用 RS 腳本,且第二次有參數。


本質上這是一個 PowerShell 代碼注入漏洞。
ManagedHost.RunScript() 使用 PowerShell.AddScript() 方法來添加要執行的命令,並且text 中的部分內容可控(參數部分)。這是典型的 PowerShell 代碼注入漏洞,使用AddScript() 會導致在調用時對 text 里的 $ 字符進行語法解析(優先將其解析為子表達式運算符)。
類似於下面這樣:
實際上漏洞觸發於第二次調用 RunScript(),調用 RS 腳本時,相應的 text 為:
可以看到傳給 AddScript() 的字符串沒有對 $ 符號進行過濾,導致了代碼注入。
觸發條件:
要想成功觸發對 RS_ProgramCompatibilityWizard.ps1 的調用,要先通過 TS_ProgramCompatibilityWizard.ps1 腳本的檢測。
觀察TS_ProgramCompatibilityWizard.ps1 腳本的代碼:
Get-DiagInput 命令就是我們前面提到的WTP PowerShell Runtime 提供的4條特殊命令之一,該命令用於從用戶獲取輸入信息。這裡獲取我們傳入的 IT_BrowseForFile 參數 並賦值給了 $selectedProgram 變量。
而後調用 Test-Selection方法來對 $selectedProgram 進行檢測:
該函數首先使用 test-path 命令來對路徑進行檢測,以保證路徑存在。然後要求路徑的擴展名為 exe 或 msi。
但是 test-path 對於使用 /../ 返回到根路徑之外的路徑會返回True,比如下面的:
這裡以 \ 開頭,表示當前盤符的根目錄,\..\ 存在,所以 \..\..\ 便超出了範圍,返回為true。也可以像c:\..\..\hello.exe 這樣。如果像原始payload 那樣以一個普通字符開頭,考慮到 TS_ProgramCompatibilityWizard.ps1 腳本所在的臨時目錄,以及 該普通字符所占一級,至少需要9個 \..\ 才可以。
然後從符號,以防代碼注入。但這行代碼其實是錯的,正確的寫法如下:
由於原來的腳本中直接使用 "" 實際會在傳給Replace之前被PowerShell 引擎解析,根本無法匹配到 $ 字符。
TS 腳本的最後,使用了Update-DiagRootCause 命令,該命令也是4條特殊命令之一,用於報告root cause 的狀態。注釋中寫道該命令會觸發調用 RS_ 腳本,-parameter 指定的字典會被作為參數傳給腳本。導致第二次調用 RunScript() 方法,並且參數中的 -TargetPath 可控,進而觸發了漏洞。
參考資料:
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/wintt/windows-troubleshooting-toolkit-portal


