Windows安装程序权限提升漏洞(CVE-2023-21800)深度解析

本文详细分析了Windows Installer(MSI)技术中的权限提升漏洞CVE-2023-21800,包括漏洞原理、利用方法、修复措施及微软的Redirection Guard防护机制,涉及环境变量污染、符号链接攻击等核心技术点。

Windows Installer EOP (CVE-2023-21800)

2023年3月21日 - 作者:Adrian Denkiewicz

TL;DR: 本篇博客文章详细介绍了我们对Windows Installer(MSI)安装技术的研究细节和方法论。如果您只对漏洞本身感兴趣,可以直接跳转到相关部分。

引言

最近,我决定研究许多流行Windows应用程序的一个共同方面——它们的MSI安装程序包。

并非所有应用程序都以这种方式分发。有些应用程序实现了自定义的引导机制,有些则只是简单地放置在磁盘上。然而,在典型的企业环境中,通常需要对安装的软件包进行某种形式的控制。使用MSI软件包简化了任何数量系统的安装过程,并提供了额外的好处,如自动修复、轻松修补和与GPO的兼容性。一个很好的例子是Google Chrome,它通常作为独立可执行文件分发,但在专用域上提供了企业软件包。

企业环境的另一个有趣方面是需要对员工账户进行严格控制。特别是在安全性良好的Windows环境中,最小权限原则确保除非有充分的理由,否则不会授予管理权限。这对恶意软件或恶意攻击者来说是个坏消息,因为他们本可以从手头拥有额外权限中受益。

在我的研究中,我想研究流行MSI软件包的安全性,并了解攻击者是否可以将它们用于任何恶意目的,特别是提升本地权限。

典型安装

MSI软件包通常需要管理权限。因此,运行恶意安装程序直接意味着游戏结束。我想研究合法的、经过正确签名的MSI软件包。要求某人输入管理员密码,然后以某种方式打开提升的cmd也是一个选项,但我在本篇博客文章中选择不讨论这一点。

让我们快速看一下安装程序文件是如何生成的。事实证明,有几种生成MSI软件包的选择。其中最流行的是一些是WiX Toolset、InstallShield和Advanced Installer。第一个是免费和开源的,但需要编写专门的XML文件。另外两个提供了各种功能集、丰富的GUI界面和客户支持,但需要额外的许可证。人们可以在这些产品中寻找通用漏洞,然而,解决所有可能的功能变化确实很困难。另一方面,这正是安装过程中可能引入实际错误的地方。

在安装过程中,将创建新文件。一些现有文件也可能被重命名或删除。各种安全对象的访问权限可能会被更改。有趣的问题是,如果存在意外的访问权限会发生什么。安装程序会失败,还是会尝试编辑权限列表?大多数安装程序还会修改Windows注册表项,随处放置一些快捷方式,并最终在事件日志、数据库或纯文件中记录某些操作。

操作列表并不是真正封闭的。MSI软件包可以实现所谓的自定义操作,这些操作在专用的DLL中实现。如果是这种情况,在那里寻找有趣的错误是非常合理的。

一旦我们准备好并安装了安装程序包,我们通常可以观察到在C:\Windows\Installers目录中缓存了一个新副本。这是一个隐藏的系统目录,非特权用户无法写入。MSI软件包的副本被重命名为随机名称,匹配以下正则表达式:^[0-9a-z]{7}\.msi$。每个机器甚至每个新安装的名称都是唯一的。要识别特定的软件包,我们可以查看文件属性(但这取决于MSI创建者决定配置哪些属性),搜索Windows注册表,或查询WMI:

1
2
3
4
5
$ Get-WmiObject -class Win32_Product | ? { $_.Name -like "*Chrome*" } | select IdentifyingNumber,Name

IdentifyingNumber                      Name
-----------------                      ----
{B460110D-ACBF-34F1-883C-CC985072AF9E} Google Chrome

通过其GUID引用软件包是我们最安全的选择。然而,同一产品的不同版本可能仍然有不同的标识符。

假设我们使用非特权用户账户,我们能用这些知识做什么有趣的事情吗?

修复过程

内置的Windows工具msiexec.exe位于System32和SysWOW64目录中。它用于管理MSI软件包。该工具是Windows的核心组件,具有悠久的漏洞历史。顺便说一句,我过去也发现过这样一个问题(CVE-2021-26415)。其选项的文档列表可以在MSDN页面上找到,尽管也实现了一些额外的未记录开关。

值得强调的标志包括:

  • /l*vx 记录任何额外细节并搜索有趣事件
  • /qn 隐藏任何UI交互。这在尝试开发自动化漏洞利用时非常有用。另一方面,潜在错误将导致新的消息框。在消息被接受之前,进程不会继续,并且可能冻结在意外状态。我们可能能够在原始访问权限重新引入之前修改一些现有文件。

修复选项部分列出了我们可以用来触发修复操作的标志。这些操作将确保坏文件被删除,好文件被重新安装。坏的定义是我们控制的东西,即我们可以强制重新安装所有文件、所有注册表项,或者只重新安装那些具有无效校验和的文件。

参数 描述
/fp 如果文件丢失,修复软件包。
/fo 如果文件丢失或安装了旧版本,修复软件包。
/fe 如果文件丢失或安装了相同或旧版本,修复软件包。
/fd 如果文件丢失或安装了不同版本,修复软件包。
/fc 如果文件丢失或校验与计算值不匹配,修复软件包。
/fa 强制重新安装所有文件。
/fu 修复所有必需的用户特定注册表项。
/fm 修复所有必需的计算机特定注册表项。
/fs 修复所有现有快捷方式。
/fv 从源运行并重新缓存本地软件包。

大多数msiexec操作需要提升权限。我们不能安装或卸载任意软件包(除非系统配置严重错误)。然而,修复选项可能是一个有趣的例外!它可能是,因为并非每个软件包都会这样工作,但找到一个会这样工作的软件包并不难。对于这些,msiexec将自动提升以SYSTEM用户身份执行必要的操作。有趣的是,一些操作仍然使用我们的非特权账户执行,这使得情况更加值得注意。

我们的账户的模拟将由于各种安全原因发生。不过,只有一些操作可以被模拟。如果你看到SYSTEM用户重命名文件,那总是一个完全特权的操作。另一方面,在分析谁确切地写入给定文件时,我们需要查看文件句柄最初是如何打开的。

我们可以使用Process Monitor等工具观察所有这些事件。为了过滤掉噪音,我建议使用下面显示的设置。可能会错过一些有趣的东西,例如子进程的操作,但一次挖掘每个事件是不现实的。另外,我故意禁用注册表活动跟踪,但偶尔值得重新启用它以查看某些操作是否不由可编辑的注册表项控制。

我推荐的另一个技巧是突出模拟和非模拟操作之间的区别。我更喜欢突出显示任何未明确模拟的内容,但您可能更喜欢反转逻辑。

然后,要开始分析上述Google Chrome安装程序的事件,可以运行以下命令:

1
msiexec.exe /fa '{B460110D-ACBF-34F1-883C-CC985072AF9E}'

事件流应由ProcMon捕获,但要寻找问题,我们需要理解什么可以被视为问题。简而言之,任何我们可以以某种方式修改的安全对象上的操作都是有趣的。SYSTEM写入我们控制的文件?那是我们的目标。

通常,我们不能直接控制受影响的路径。然而,我们可以用符号链接替换原始文件。常规符号链接可能对非特权用户不可用,但我们可以使用一些技巧和工具在Windows上重新发明该功能。

Windows EoP原语

虽然我们不是试图从每个发现的漏洞中弹出shell,但教育读者在一些权限提升原语下可能实现什么是有趣的。

通过任意文件创建漏洞,我们可以通过创建DLL来攻击系统,系统进程之一将加载该DLL。这稍微困难一些,但并非不可能,找到一个Windows进程在不重新启动整个系统的情况下加载我们植入的DLL。

拥有任意文件创建漏洞但无法控制内容,我们弹出shell的机会大大减少。不过,我们仍然可以使Windows无法操作。

通过任意文件删除漏洞,我们至少可以破坏操作系统。不过,通常我们也可以将其转变为任意文件夹删除,并使用Abdelhamid Naceri发现的复杂方法实际弹出shell。

可能的原语列表很长且引人入胜。尽管如此,单个EoP原语应被视为严重的安全问题。

一个统治一切的漏洞 (CVE-2023-21800)

我在许多测试的MSI软件包中观察到了相同的有趣行为。这些软件包由不同的MSI创建者使用不同类型的资源创建,基本上没有任何共同点。然而,它们都遵循相同的模式。即,非特权用户设置的环境变量也在由修复操作调用的SYSTEM用户的上下文中使用。

虽然我最初认为应用程序错误地信任了一些环境变量,但事实证明Windows Installer的回滚机制负责不安全操作。

7-zip

7-Zip提供专用的Windows安装程序,发布在项目页面上。测试了以下文件:

文件名 版本
7z2201-x64.msi 22.01

为了更好地理解问题,我们可以研究应用程序的源代码。安装程序,在DOC/7zip.wxs文件中定义,引用ProgramMenuFolder标识符。

1
2
3
4
5
6
7
8
9
<Directory Id="ProgramMenuFolder" Name="PMenu" LongName="Programs">
   <Directory Id="PMenu" Name="7zip" LongName="7-Zip" />
</Directory>
...
<Component Id="Help" Guid="$(var.CompHelp)">
   <File Id="_7zip.chm" Name="7-zip.chm" DiskId="1" >
       <Shortcut Id="startmenuHelpShortcut" Directory="PMenu" Name="7zipHelp" LongName="7-Zip Help" />
   </File>
</Component>

ProgramMenuFolder后来用于存储一些组件,例如指向7-zip.chm文件的快捷方式。

如MSDN页面上所述:

安装程序将ProgramMenuFolder属性设置为当前用户的Program Menu文件夹的完整路径。如果存在"All Users"配置文件并且ALLUSERS属性已设置,则此属性设置为"All Users"配置文件中的文件夹。

换句话说,该属性将指向由当前用户控制的目录(如先前示例中的%APPDATA%),或指向与"All Users"配置文件关联的目录。

虽然第一种配置不需要额外解释,但第二种配置很棘手。通常使用C:\ProgramData\Microsoft\Windows\Start Menu\Programs路径,而C:\ProgramData甚至可由非特权用户写入。C:\ProgramData\Microsoft路径被适当锁定。这给我们留下了安全的默认设置。

然而,调用修复过程的用户可能故意修改(即污染)PROGRAMDATA环境变量,从而将"All Users"配置文件重定向到用户可写入的任意位置。setx命令可用于此目的。它修改与当前用户关联的变量,但重要的是要强调只有未来的会话会受到影响。应启动一个全新的cmd.exe实例以继承新设置。

可以将符号链接放置在%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\7-zip\目录中作为预期文件之一,而不是放置合法文件。结果,修复操作将:

  1. 删除任意文件(使用SYSTEM权限)
  2. 尝试恢复原始文件(使用非特权用户账户)

第二个操作将失败,导致任意文件删除原语。这可以在以下捕获中观察到,假设我们目标是先前创建的C:\Windows\System32__doyensec.txt文件。我们故意在C:\FakeProgramData\Microsoft\Windows\Start Menu\Programs\7-zip\7-Zip Help.lnk路径下创建了指向目标文件的符号链接。

首先,我们可以看到导致REPARSE状态的操作。文件被简要处理(或者更确切地说,其属性被处理),并对其调用SetRenameInformationFile。重命名部分有些误导。实际发生的是文件被移动到不同的位置。这是Windows安装程序在出现问题时创建回滚指令的方式。如前所述,SetRenameInformationFile不在文件句柄级别工作,并且不能被模拟。此操作以完整的SYSTEM权限运行。

后来,我们可以发现尝试恢复原始文件,但使用模拟令牌。这些操作导致ACCESS DENIED错误,因此目标文件保持删除状态。

在众多其他安装程序中观察到了相同的序列。例如,我与PuTTY的维护者合作了一个可能的解决方法,该方法在0.78版本中引入。在该版本中,仅当提供管理员凭据时才允许提升的修复。然而,这在功能上并不等同,并引入了一些其他问题。0.79版本应恢复旧的WiX配置。

重定向防护

该问题直接报告给Microsoft,并提供了所有上述信息和专用漏洞利用。Microsoft为其分配了CVE-2023-21800标识符。

它在最新版本的Windows 10和Windows 11上可重现。然而,它不符合赏金资格,因为攻击在Windows 11开发者预览版上已经被缓解。相同的缓解措施已通过2022-02-14更新启用。

2022年10月,Microsoft在Windows 10和Windows 11上发布了一项称为重定向防护的新功能。该更新引入了一种称为ProcessRedirectionTrustPolicy的新型缓解措施和相应的PROCESS_MITIGATION_REDIRECTION_TRUST_POLICY结构。如果为给定进程启用了缓解措施,所有处理的连接点都会经过额外验证。验证首先检查文件系统连接点是否由非管理员用户创建,如果是,则策略是否阻止跟随它们。如果操作被阻止,则返回错误0xC00004BC。由管理员用户创建的连接点明确允许为具有更高信任级别标签。

在初始轮次中,重定向防护为打印服务启用。2022-02-14更新在msiexec进程上启用了相同的缓解措施。

这可以在以下ProcMon捕获中观察到:

msiexec是少数默认强制执行此缓解措施的应用程序之一。要自行检查,请使用以下不太好的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <windows.h>
#include <TlHelp32.h>
#include <cstdio>
#include <string>
#include <vector>
#include <memory>

using AutoHandle = std::unique_ptr<std::remove_pointer_t<HANDLE>, decltype(&CloseHandle)>;
using Proc = std::pair<std::wstring, AutoHandle>;

std::vector<Proc> getRunningProcesses() {
    std::vector<Proc> processes;

    std::unique_ptr<std::remove_pointer_t<HANDLE>, decltype(&CloseHandle)> snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0), &CloseHandle);

    PROCESSENTRY32 pe32;
    pe32.dwSize = sizeof(pe32);
    Process32First(snapshot.get(), &pe32);

    do {
        auto h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID);
        if (h) {
            processes.emplace_back(std::wstring(pe32.szExeFile), AutoHandle(h, &CloseHandle));
        }
    } while (Process32Next(snapshot.get(), &pe32));

    return processes;
}

int main() {
    auto runningProcesses = getRunningProcesses();

    PROCESS_MITIGATION_REDIRECTION_TRUST_POLICY policy;

    for (auto& process : runningProcesses) {
        auto result = GetProcessMitigationPolicy(process.second.get(), ProcessRedirectionTrustPolicy, &policy, sizeof(policy));

        if (result && (policy.AuditRedirectionTrust | policy.EnforceRedirectionTrust | policy.Flags)) {
            printf("%ws:\n", process.first.c_str());
            printf("\tAuditRedirectionTrust: % d\n\tEnforceRedirectionTrust : % d\n\tFlags : % d\n", policy.AuditRedirectionTrust, policy.EnforceRedirectionTrust, policy.Flags);
        }
    }
}

重定向防护应防止整个类别的连接点攻击,并可能显著复杂化本地权限提升攻击。虽然它解决了先前提到的问题,但它也解决了其他类型的安装程序错误,例如当特权安装程序从用户控制的目录移动文件时。

Microsoft披露时间线

状态 数据
漏洞报告给Microsoft 2022年10月9日
漏洞被接受 2022年11月4日
补丁开发 2023年1月10日
补丁发布 2023年2月14日

其他相关文章:

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计