文章总结: WindowsInstaller存在CVE-2020-0911提权漏洞,允许普通用户通过修改设备映射和对象符号链接重定向MSI文件访问路径,使特权进程执行恶意操作。漏洞利用涉及创建对象目录结构、重定向C:驱动器映射、修改MSI文件以创建注册表项符号链接,最终获取SYSTEM权限。微软已在2020年9月安全更新中修复此漏洞,建议用户及时更新系统。
综合评分: 85
文章分类: 漏洞分析,提权漏洞,Windows安全,漏洞利用,红队技术
Windows Installer 提权漏洞分析
aeverj
红队工坊
2025年11月27日 08:01
北京
翻译自 Windows Installer arbitrary content manipulation Elevation of Privilege (CVE-2020-0911)
概述: Windows 安装程序(Windows Installer)在访问 C:\Windows\Installer 目录下的 MSI 文件时,会模拟用户身份(并使用该用户的设备映射),同时信任这些文件来执行提升权限的操作,比如创建注册表项。普通用户可以利用这一点获取 SYSTEM 权限。
这个漏洞已在 2020 年 9 月的 Windows 安全更新中修复。
引言
几个月前,[Adrian Denkiewicz]https://twitter.com/a_denkiewicz 发现的一个有趣的 [Windows Installer 漏洞]https://blog.doyensec.com/2023/03/21/windows-installer.html 让 [Jonas Lykkegård]https://twitter.com/jonasLyk/ 想起了他在 2020 年发现的一个漏洞,当时我帮忙参与了漏洞利用的开发。虽然这个漏洞有点年头了(2020 年 9 月就修复了),但它依然很有意思。一方面,它不同于常见的”敏感资源可被用户写入”或”特权进程在未模拟用户身份的情况下访问用户资源”这类漏洞,而是出在 msiexec 在模拟用户身份时执行的操作上。另一方面,它的利用过程也颇为巧妙——而且很有趣。
所以,迟到总比不到好,这就是 CVE-2020-0911 的技术分析。
注:本文假设读者对文件系统重定向攻击有一定了解,大部分必要的概念在[这篇文章]https://offsec.almond.consulting/intro-to-file-operation-abuse-on-Windows.html中有介绍。
漏洞描述
Windows 安装程序(msiexec.exe)在安装软件包时,会在 C:\Windows\Installer 目录下为托管包创建 MSI 文件。这些文件普通用户无法修改,当需要对相关产品执行维护操作时,安装程序会在一定程度上信任这些文件。
但问题来了:当普通用户触发对 C:\Windows\Installer 中某个 MSI 文件的维护操作时,Windows 安装程序会在模拟用户身份的情况下访问该 MSI 文件,并使用被模拟用户的_设备映射_(device map)。
关于设备映射的详细解释,可以参考 [James Forshaw]https://twitter.com/tiraniddo/ 关于 [TrueCrypt 驱动漏洞的文章]https://googleprojectzero.blogspot.com/2015/10/windows-drivers-are-truely-tricky.html(他在[符号链接文章]https://googleprojectzero.blogspot.com/2015/08/windows-10hh-symbolic-link-mitigations.html和 [DefineDosDevice 研究]https://googleprojectzero.blogspot.com/2018/08/windows-exploitation-tricks-exploiting.html中也提到过),以及 [itm4n]https://infosec.exchange/@itm4n 的[这篇 LSA PPL 绕过文章]https://itm4n.github.io/bypassing-lsa-protection-userland/#ms-dos-device-names。
简单来说:设备映射是一组符号链接(在对象管理器中,不是文件系统中),它将 MS-DOS 设备名称(如 C: 或 D:)映射到相应的设备对象(例如 \Device\HarddiskVolume1)。这样当使用 C:\Dir\File.ext 这样的路径时,C: 就会被解析到对应的设备。虽然系统有一个”全局”设备映射,但每个会话也有自己的设备映射,允许用户做一些事情,比如将网络驱动器映射到 Z:,这个映射只存在于该用户的会话中。关键是,普通用户进程可以在其会话的设备目录中创建一个 C: 对象符号链接,从而重定向该会话中其他进程的后续文件访问——以及模拟该用户的进程。
回到 CVE-2020-0911 中 Windows 安装程序的行为,我们看到特权 msiexec 进程在模拟普通用户身份时访问 .msi 文件:
然而,特权进程后续执行的操作(不再模拟用户)显然是基于它从 .msi 文件中读取的数据,比如创建/覆盖注册表项:
所以,读取和解析 MSI 文件是在模拟普通用户身份时进行的,但基于从 MSI 文件读取的数据执行的后续操作却是以完整权限进行的。
普通用户可以利用这一点,通过修改用户设备映射,使用对象目录和对象符号链接将 MSI 文件访问重定向到任意文件,从而让 Windows 安装程序以 SYSTEM 权限执行任意维护操作——比如创建注册表项。
漏洞利用
利用这个漏洞并不像简单创建一个符号链接那么直接,因为有一些障碍需要克服:
- 修改当前会话的
C:映射会对该会话中的其他进程产生副作用,可能导致它们运行异常(最终使会话不可用) - 在
C:\和C:\Windows这样经常被访问的目录中进行大规模文件重定向也会出现同样的问题:这可能会在目标进程到达关键点之前就让它崩溃,也会影响其他进程(如果它们使用相同的设备映射) - 修改后的 MSI 文件不能与原始文件差异太大(仍需匹配产品 ID 等),否则安装程序会报错并停止
为了解决前两个限制,Jonas 想出了一个巧妙的设备映射重定向方法:
-
在对象管理器中创建一个复制目标文件结构的目录结构:
-
使用对象目录和符号链接指向原始文件
-
使用对象符号链接将目标文件重定向到所需文件
-
使用[影子对象目录]
https://googleprojectzero.blogspot.com/2016/08/a-shadow-of-our-former-self.html,使对结构中其他文件的访问透明地回退到原始文件/文件夹 -
将
\GLOBAL??的全局设备映射应用到所有当前(非目标)进程,这样它们不会受到重定向的影响 -
将会话的设备映射重定向到伪造的目录结构
-
所有新进程将使用”伪造”的设备映射
-
触发目标进程
目标 MSI 文件 C:\Windows\installer\c63eb.msi 的重定向涉及多个部分,所以逐步查看会更容易理解。最初,会话的 DosDevice 对象目录只包含一个指向 Global?? 对象目录的链接:
通过在这个目录中创建一个 C: 对象链接,对 C:\whatever 的访问(在此会话中)会被重定向到文件系统上的一个临时文件夹:
这个临时文件夹包含以原始 C:\ 根目录中所有文件夹命名的联接点(junction):
这些联接点都指向相应的原始文件夹(使用 Global 设备映射),除了一个:即我们想要重定向的路径的一部分(这里是 Windows,对应 C:\Windows):
这个联接点指向 \RPC Control 中的一个对象目录。这个”主”对象目录只包含一个条目:对应目标路径中下一个项目的链接(这里是 installer,对应 C:\Windows\installer):
这个”主”对象目录有一个[影子对象目录]https://googleprojectzero.blogspot.com/2016/08/a-shadow-of-our-former-self.html(这里称为”次”目录),当在”主”目录中找不到资源时,会使用它作为后备。”次”目录包含原始 C:\Windows 文件系统文件夹中所有项目的链接,每个链接都指向文件系统上的原始项目:
对象目录和影子对象目录的这种组合允许在主目录中重定向特定的文件或文件夹,同时将所有其他条目重定向到影子/次目录中各自的原始文件系统对应项:当在主目录中找不到某个项目时,就会使用次目录。虽然不是严格必需的(我们可以只使用一个对象目录并更改其中的目标符号链接),但在某些场景下,这使得添加和更改链接更容易一些。
主目录中的 installer 符号链接指向另一对对象目录(主和次),这次反映的是原始的 C:\Windows\installer 文件系统文件夹:
在这对目录的主目录中,c63eb.msi 链接指向我们希望 Windows 安装程序加载的恶意文件(而它以为加载的是受信任系统目录中的文件),这里是 C:\Temp\c63eb.msi。(注:inprogressinstallinfo.ipi 最初在测试期间用于 oplock,但最终证明不是必需的。)
下图总结了重定向设置:
在实施这个重定向之前,当前会话中的现有进程会通过对 NtSetInformationProcess 的未公开调用被设置为使用 \Global?? 目录作为它们的设备映射,该调用允许为进程设置设备映射:
bool SetProcessDeviceMap(HANDLE hDir, HANDLE hProc = GetCurrentProcess()) {
PROCESS_DEVICEMAP_INFORMATION DeviceMap = { hDir };
NTSTATUS status = NtSetInformationProcess(hProc,
ProcessDeviceMap,
&DeviceMap,
sizeof(DeviceMap));
return status == 0;
}
这允许其他进程使用全局设备映射继续正常运行(只要它们不需要访问会话特定的设备),但新进程(在会话中或模拟它)将使用伪造的目录结构。同样,对于这个进程,对现有文件和文件夹的访问将基本正常工作,除了特定的目标文件 C:\Windows\installer\c63eb.msi 会被重定向到 C:\Temp\c63eb.msi。
至于 MSI 文件,在 PoC 中我们选择直接”二进制补丁”现有的 MSI 文件,替换一个会被创建的注册表项,为 WerFault.exe 创建一个 Image File Execution Options 注册表项,然后触发崩溃以 SYSTEM 身份执行载荷(WerFault.exe 以 SYSTEM 身份运行,然后以崩溃进程的用户身份再次运行)。
唯一的要求(几乎总是满足)是机器上至少安装了一个产品/托管包。在我们的 PoC 中,我们针对的是 Visual C++ 2019 可再发行组件 x64 的最小运行时包,因为它非常常见。但请注意,这个漏洞显然不是特定于这个包的,同样的方法几乎可以用于任何 MSI 包。
因为目标注册表路径比被替换的路径长,所以使用了[注册表项符号链接]https://googleprojectzero.blogspot.com/2015/08/windows-10hh-symbolic-link-mitigations.html来保持注册表路径简短:
$RegSymlink = [NtApiDotNet.NtKey]::CreateSymbolicLink("\Registry\Machine\SOFTWARE\Microsoft\Tracing\a\abcdefghijklmnop", $null, "\REGISTRY\Machine\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options")
这创建了一个到所需注册表路径的替代路径 HKLM:\SOFTWARE\Microsoft\Tracing\a\abcdefghijklmnop\WerFault.exe,替代路径与 MSI 文件中使用的现有路径 HKLM:\SOFTWARE\Microsoft\DevDiv\VC\Servicing\14.0\RuntimeMinimum 长度相同。这简化了所需的二进制补丁,因为现在可以使用简单的字符串匹配/替换。
总结一下,选择的利用方法如下:
- 查找并复制现有的 MSI 文件
- 创建一个注册表项符号链接到目标注册表项和值(
WerFault.exe的Image File Execution Options注册表项的Debugger属性) - 修补二进制 MSI 文件以替换注册表项/条目路径、名称和值
- 更改用户设备映射,通过对象目录和对象符号链接重定向
C:、C:\Windows和C:\Windows\Installer,并更改现有进程的设备映射,使它们继续正常运行 - 在
C:\Windows\Installer中的 MSI 文件上运行msiexec(但由于重定向,实际访问的是修改后的文件) - 提升权限的
msiexec将使用伪造的 MSI 文件(认为它是受信任的文件)并创建所需的注册表项 - 触发崩溃以
SYSTEM身份执行WerFault.exe——以及它的”调试器”,即载荷
可以使用 Procmon 确认进程访问了重定向的路径:
并创建了所需的注册表项:
概念验证
PoC 利用这个漏洞创建所需的注册表项,然后触发崩溃以 SYSTEM 身份执行与 WerFault.exe 关联的调试器(C:\x\z.exe,它是 payload.exe 的副本)。
相关的源代码可以在[这里]https://github.com/AlmondOffSec/PoCs/tree/master/Windows_msiexec_eop找到。
注:此仓库中提供的源代码是发送给微软的原始代码;可能需要调整,因为 C++ 代码需要 [Jonas 的漏洞利用工具包]https://github.com/jonaslyk/exploitkitpub,该工具包当时不是公开的,可能与发布的版本略有不同。
PoC 针对的是 VC++ 2019 最小运行时 x64 包。如上所述,该漏洞与这个特定包无关——但选择这个包进行演示是因为它非常常见。
修复
微软已发布补丁和[公告]https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2020-0911。用户应通过常规渠道应用更新。
参考资料
https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2020-0911
如果你对网络安全、红队攻防技术充满热情,渴望学习更多实战技巧,例如渗透测试、自动化脚本编写、免杀技术等, 欢迎关注我的公众号
在这里,我会持续分享更多高质量的技术文章,与你一同探索网络安全的奥秘,提升实战技能! 让我们一起在队攻防的道路上,不断精进,突破边界!
免责声明: 本文仅供安全技术研究与学习交流之用。 严禁将本文所提及的技术用于任何非法用途,包括但不限于未经授权的渗透测试、网络攻击、恶意代码传播等。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:红队工坊 aeverj《Windows Installer 提权漏洞分析》