STATEMENT
声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
前言
实际上算是 CVE-2021-40444 的高级利用方式,而且通过利用 MSDT 的 Windows 本地 PowerShell 命令注入漏洞。达到命令执行的目的。(顺便提权?)
浅尝
首先用 GitHub 上的 POC [^1]进行测试。
看到 localhost 就知道弄错了。。重来。。不过这里有点小坑啊,这个脚本写的兼容性存在问题,原本可能是为 windows 设计的吧。。而且设定host的参数--host压根就不能用,只能用--url指定主机。
首先是解压时没考虑linux主机。话说可以直接zipfile在内存编辑压缩包的。。
然后是url参数控制访问地址,host参数控制本地监听,但是如果url没设置,远程就是默认的 localhost,那不如放在一起设置一个参数好了。。。
可以从命令行返回结果看到。。
好在一番周折之后,成功命令执行了。
行为
在虚拟机执行了远程命令之后,返回恶意 WEB 服务器,可以看到恶意的 WEB 服务接收到了虚拟机发起的请求。
有一说一,居然发起了 12 次请求。
通过ProcMon也可以看到 12 次 Tcp 连接。
在进程链中可以找到WINWORD.EXE启动了msdt.exe进程,而且带上了一串参数。但是计算器居然是一个叫做 sdiagnhost.exe 的应用程序拉起的,中间出现了断链,无法跟踪到。
WTP
回过头来,看我们的恶意 WEB 界面。在这里可以看到我们 msdt 中的参数,说明参数是通过恶意 WEB 界面传递到WORD中,结果WORD拉起了 msdt.exe 去执行。
至于如何拉起的 msdt,则是因为 ms-msdt 协议的本地执行需要 msdt.exe,从进程行为可以看到查询 MS-MSDT协议如何处理,而且还能从注册表中看到默认处理的程序正是 msdt.exe。
之前自己用来 Bypass UAC 进行本地提权就是利用的 MSDT,没想到如今 MSDT 居然可以命令执行,说明当时研究的还是不彻底。MSDT作用在 Windows 自动化检测、修复故障的过程中,例如我们耳机突然间没有声音了,它会自动辅助我们进行声音系统的检测,测试软、硬件问题。Microsoft 在这个问题上所采用的方式是 WTP (Windows Troubleshooting Platform)[^2]。
在官方介绍 About-WTP 中有整个 WTP 系统的工作流程,其中提到有一个托管的 Windows PowerShell Runtime。那么可以大胆猜测这就是 PowerShell 命令注入的地点。下图是对微软的介绍进行理解后对原图进行了复刻所做出的示意图。
在图中可以看到,传递事件到 msdt.exe 时,msdt 仅通过 CMD 接收到了一串命令行参数。参数内容其实很容易理解,处理一个现有的 PCWDiagnostic 异常,自动跳过下一步(/skip force),然后附加上了一个复杂的参数,也就是我们最核心的 Payload。msdt.exe 其实都没干啥,随后便将具体的工作交给了 sdiagnhost.exe,而 sdiagnhost.exe 则使用 PowerShell 脚本对异常进行处理。这里其实也能解释,我们的命令触发之后,进程调用链看起来直接中断,跳转到系统调用 sdiagnhost.exe 执行的 PowerShell 命令打开的计算器。
分析
随便分析一下好了,首先从命令执行入手,不过 msdt.exe 和 sdiagnhost.exe 都是 VC 程序。。
使用 IDA 看一下,可以看到进入了 CScripteDiagNativeHost::RunScript函数,然后传递了某参数进入执行?
没办法呀,看不到动态获取的值和也没有猜出来结构体的类型和具体数值,直接上调试器看看,首先设置映像的调试器,偷懒直接用 gflags.exe 设置居然失败了。。。只好手动设置一下。
两者本身效果是一样的。
然后我们当然就逮到了所执行的脚本
在脚本中也找到了我们所传入的对应参数。
获取我们输入的含有 Payload 的参数 IT_BrowseForFile。
可以看到原本还对我们的参数进行过测试,但是结果替换"$"为"`$"好像没没啥作用[^3]?应该是还需要对"`"再加一个转义。。。
可以进入 Test-Selection 函数,看到 Test-Selection 函数还对我们的输出进行了检查,这回答了我们 Payload最后为什么需要“.exe”或者“.msi”结尾。
在脚本的最后,我们的这俩参数被传入 Update-DiagRootCause 中执行,不过在文件中没有找到这个函数,回想到上面的 WTP 结构,这个家伙不知道在哪里找了,不过没有关系,我们直接调试这段 PowerShell 的执行就好了。
注:这个使用了Update-DiagRootCause 命令,该命令也是4条特殊命令之一,用于报告root cause 的状态。注释中写道该命令会触发调用 RS_ 脚本,-parameter 指定的字典会被作为参数传给脚本。导致第二次调用 RunScript() 方法,并且参数中的 -TargetPath 可控,进而触发了漏洞。[^4]
这里可不是指的调试 PowerShell 脚本,而是直接利用 PoerShell 是由 C# 驱动的,直接调试所依赖的 DLL 和对应函数。
既然是调试 .Net 程序,肯定要用到无敌的 dnSpy,设置一下 Debugger。
我也不知道为啥要设置这俩系统变量,我看到别人要设置[^4]。。就跟着设置一下 dnSpy 调试所需的变量。。。
然后呢,我们在启动 sdiagnhost.exe 的时候断住还不行,这时候我们还要找到上面执行脚本时所进入的函数。刚好根据脚本中的提示,我们找到 Microsoft.Windows.Diagnosis.SDHost.dll 的 RunScript 函数,下断点。接着点击继续,正好断住。
调试代码中,可以看到,上面 PowerShell 的两个被我们用户控制的参数,传递到 this.m_Ps.Commands.AddScript(text) 中作为参数,而没有过滤,或者说过滤的不充分,所以达到了命令注入的目的。
至此,整个利用链清晰了。首先利用CVE-2021-40444漏洞的原理,加载远程模板,该远程模板包含 ms-msdt 协议,使得WORD拉起 msdt.exe,进行含参调用,在处理参数的时候,由于微软的疏忽,导致 PowerShell 命令被攻击者污染,结果被 msdt.exe 按照 WTP 框架传递进 sdiagnhost.exe,最终 执行了恶意的 PowerShell 代码。
其他
一直摸鱼到最近才看这个漏洞,一是因为之前摸过msdt,不愿意再去看,二是一翻分析文章就能看到yara规则的查杀,实在心累~~(就不能让孩子试试钓鱼嘛?打不动点呀)~~
不过为了优雅,哥们儿重构一下 Python 的利用脚本,顺便用自己的简化 Payload。(重复造轮子)
参考文献
1.CVE-2022-30190-follina-Office-MSDT-Fixed
2.Windows Troubleshooting Platform | Microsoft Docs
3.CVE-2022-30190 MSDT 代码注入漏洞分析 (seebug.org)
4.CVE-2022-30190:Microsoft Windows 支持诊断工具 (MSDT) 远程代码执行
安恒信息
✦
杭州亚运会网络安全服务官方合作伙伴
成都大运会网络信息安全类官方赞助商
武汉军运会、北京一带一路峰会
青岛上合峰会、上海进博会
厦门金砖峰会、G20杭州峰会
支撑单位北京奥运会等近百场国家级
重大活动网络安保支撑单位
END
长按识别二维码关注我们