本博客介绍两个连环漏洞:第一阶段是由 ROOT 驱动器重新映射导致的 DLL 劫持漏洞,第二阶段是由 CSRSS 服务器管理的激活缓存毒化漏洞。
第一阶段在 Ekoparty 2023 上由 BlueFrost Security 的 Nicolás Economou 在题为 “I'm High” 的演讲中详细展示。他解释了如何利用当时尚未被 Microsoft 修补的漏洞。该漏洞允许 MEDIUM INTEGRITY 用户提升至有限的 HIGH PRIVILEGES,但无法获得完整的 Administrator 权限。
第二阶段并未在该会议上展示,不过提供了开始研究的一些步骤提示。
首先,我们将回顾第一阶段以提供背景介绍。然后深入探讨我对第二阶段的研究,详细说明如何从有限的 HIGH INTEGRITY 完全提权至 Administrator。这包括针对所有 Windows 版本的两个阶段完整可用的 PoC,已在 Windows 10、Windows 11、Windows Server 2022 和 Windows Server 2019(已应用所有更新)上成功测试。

该阶段唯一的要求是初始进程以 MEDIUM INTEGRITY LEVEL 启动,且用户属于 Administrator 组。
第一阶段利用可总结为以下步骤:
例如:将磁盘从 "C:\" 重新映射到 "C:\users\public"
这也会将文件夹 "system32" 从 "C:\windows\system32" 重新映射到 "C:\users\public\windows\system32"
其中一个受影响的程序是 CTFMON,它以 HIGH INTEGRITY LEVEL 运行,但不具备 Administrator 权限。
正常情况下,它会尝试从真实的 system32 文件夹加载名为 MsCtfMonitor.dll 的模块,但由于 ROOT 驱动器被重新映射,它会在我们控制的假 system32 中查找 MsCtfMonitor.dll,我们可以在其中创建并放置一个同名特制 DLL。
此时,通过在假 system32 文件夹中放置我们版本的 MsCtfMonitor.dll,其 DoMsCtfMonitor 函数被调用,并在 HIGH INTEGRITY LEVEL 下执行我们的代码。




同时,我们可以确认,尽管进程处于 HIGH INTEGRITY LEVEL,但并不具备 Administrator 权限:


在他的 Ekoparty 演讲中,Nicolas 建议了以下步骤来完成利用:


虽然这看起来简单,但需要大量时间进行逆向和调试。
进一步研究这个攻击向量故事后,发现激活上下文缓存的毒化已在一些漏洞利用中被使用。因此,了解之前如何进行利用是有价值的,可以提供额外的背景和见解。关于此利用的详细信息可通过 Zero Day Initiative 的文章 毒化激活上下文缓存:利用 CSRSS 进行权限提升 获取。
激活缓存的使用发生在程序即将加载需要特定版本的库时。
例如,如果一个应用程序要加载 C:\Windows\System32\comctl32.dll,不能保证该位置的 comctl32.dll 是应用程序所需的版本。这是激活上下文缓存的一个基本用例。程序可以向 CSRSS 服务器发送请求,以处理一个新的激活上下文条目并添加到缓存中,这样程序就可以加载所需的特定库版本。
为此,使用了所谓的清单,它是 XML 格式的。通常作为资源嵌入在 EXE 或 DLL 文件中。或者,Windows 会在程序可执行文件所在的文件夹中搜索清单文件。
上面提到的 URL 包含一些旧漏洞利用使用的清单文件示例,例如诱使系统从攻击者通过 PATH TRAVERSAL 技术到达的受控目录加载库 advapi32.dll。

当然,一些使用的攻击向量已被修补,同时发现了新技术。此外,在 Windows 11 22H2 的 2022 年 10 月补丁中,添加了一个新的检查。
实施此补丁后,当注册 激活上下文 (ACTX) 时,只有当将新条目添加到缓存中的进程的 RID 大于或等于使用该条目的进程的 RID 时,才能绕过检查。
在 winnt.h 中我们可以看到 RID 值:

绕过此检查的提议是从运行特制 DLL 的 CTFMON 进程创建一个包含 激活上下文 的请求。此特制 DLL 的 RID=0x3000,在条目添加到缓存后,TCMSETUP(RID=0x3000)将加载 tapi32.dll。
在我尝试按照步骤操作的过程中,我尝试了所有可能的组合来使用 CreateActCtx 注册 ACTX。这被证明是不可能的,因为总有一个检查阻止它。
需要注意的是,此函数位于用户态,由 kernel32.dll 导出。可以通过在内存中修补 DLL 来绕过检查,这不太优雅,但可行且应该有效。

Nicolas 的演讲幻灯片建议使用 LOW LEVEL。然而,注意到那个眨眼表情,很明显在没有补丁的情况下利用此漏洞时,使用 CreateActCtx 不是 更好的选择。

高级本地过程调用 (ALPC) 是一种用于在 Windows 操作系统内高速发送消息的跨进程通信机制。与标准 Windows API 不同,ALPC 不直接对应用程序可用。相反,它是一种内部机制,只能由 Windows 操作系统的组件访问。(以及我们。)
经过进一步研究,我注意到一些旧的缓存毒化漏洞利用使用了 ALPC 与服务器直接通信。例如在 Philip Tsukerman 的文章 激活上下文——一个爱情故事 中可以看到。
CsrClientCallServer 函数实现了 Win32 进程与 CSRSS 进程之间的 ALPC 接口。
因此,应该尝试使用 CsrClientCallServer 调用作为服务器的 CSRSS 进程。
在寻找旧漏洞利用中的示例时,我在 Packet Storm 上找到了一个关于 相关堆缓冲区溢出问题 的页面。
当使用正确的包调用 CSRSS 服务器时,它会被 BaseSrvSxsCreateActivationContextFromMessage 函数接收,该函数属于模块 sxssrv.dll。
该函数只有一个参数:指向接收到的包的指针。为了逆向它,我创建了一个自定义的 TotalMessage 结构。
TotalMessage 结构包的前 0x40 字节是 HEADER,之后是嵌入的 激活上下文消息,其结构为 _BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG。
TotalMessage 结构如下所示:

这里是 _BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG 结构:

在此结构内部有六个 UNICODE_STRINGS,分别对应语言或 CultureFallbacks、AssemblyDirectory、TextualAssemblyIdentity、AssemblyName,以及两个 _BASE_MSG_SXS_STREAM 结构,每个结构内部包含一个 UNICODE_STRING。
以下是 _BASE_MSG_SXS_STREAM 结构:

鉴于创建服务器接受的有效包的难度,值得详细说明如何做到。
_BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG 内部的 Flags 字段值非常重要,因为有很多组合。没有正确的标志值,就无法利用此漏洞。
例如,以我的 MsCtfMonitor.dll 代码为例。经过多次尝试,我得出结论,此漏洞利用的正确 flags 值是 0x41:

不同值的组合可能导致错误的路径标志值:

相同的 TotalMessage 结构将具有大小为 0x40 字节的头部。剩余的 0x1f8 字节保留给 _BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG 结构:
struct TotalMessage
{
signed __int64 pad[8];
_BASE_SXS_CREATE_ACTIVATION_CONTEXT_MSG message;
};
分配大小为 0x40+0x1f8:

然后我组合了字符串,并为 tapi32.dll 执行了激活缓存上下文。这是一个非常少用的 DLL,由名为 TCMSETUP 的进程加载。它具有 HIGH PRIVILEGES INTEGRITY LEVEL(RID=0x3000),权限与 Administrator 相同。

在我的 DLL 代码中,调用了 CaptureUnicodestring 函数。这最终会调用 CsrCaptureMessageString:
NTSTATUS CaptureUnicodeString(LPVOID CaptureBuffer, PSTR OutputString,