调用和注册——针对 WinReg RPC 客户端的中继攻击
执行摘要
Akamai 研究人员 Stiv Kupchik 在 Microsoft 的 Remote Registry 客户端中发现了一个新的权限提升 (EoP) 漏洞 CVE-2024-43532,其 CVSS 评分为 8.8。
该漏洞会滥用 WinReg 客户端实施中的一种回退机制。在 SMB 传输不可用的情况下,该机制会使用已作废的传输协议,而这种方法并不安全。
利用这一漏洞,攻击者可以将客户端的 NTLM 身份验证详细信息中继到活动目录证书服务 (ADCS),并请求一份用户证书以用于在域中执行进一步的身份验证。我们在 GitHub 存储库中提供了概念验证。
本着负责任的态度,我们于 2024 年 2 月将该漏洞的信息披露给了 Microsoft 安全资源中心。该漏洞已在 2024 年 10 月的 Patch Tuesday 中得到了修补。
所有未作修补的 Windows 版本都会受到该漏洞的影响。
此外,我们还在 No Hat 计算机安全大会上介绍了我们的发现。
简介
MS-RPC 是指 Microsoft 的远程过程调用 (RPC) 协议实施及标准。RPC 是一种进程间通信机制,它允许进程公开一些可供其他进程调用的功能。RPC 是 Windows 操作系统的核心组成部分,许多服务都依靠它来工作,包括服务管理器以及 在 wininit 中关闭等等。
我们的团队围绕 MS-RPC 开展了大量研究,包括 攻击性 研究和 防御性 研究。在前一项研究中,我们发现了一种缓存攻击形式的大规模攻击媒介,在后一项研究中则分析了它的安全机制。
这次,我们希望换个角度来探究一下 RPC。任何允许在不同计算机之间进行通信和操作的协议都必然会涉及到用户身份验证,事实上,RPC 协议支持将凭据和身份验证的传递作为其 绑定进程的一部分来完成。但是,只要存在身份验证,就有发生身份验证中继的可能性,因此我们希望找出在何处可能会有机会进行中继。
RPC、身份验证及其中间过程
RPC 会话通过 绑定来进行处理。客户端应用程序连接至服务器应用程序,绑定至所需的 RPC 接口,然后请求运行某个特定函数(图 1)。
按照连接的要求,绑定请求和响应可能会在多个数据字段之间转移。正常情况下,如果不涉及身份验证,RPC 绑定只会用于决定 传输语法 ,而该语法将用于封装后续调用中的函数参数。
那么,这一交互过程中缺少了什么呢?当然是身份验证!服务器怎样才能确认客户端的身份以及是否允许该客户端执行所请求的操作呢?答案是它没有办法确认,除非客户端特地在绑定中添加了安全上下文(即身份验证)。默认情况下,所有 RPC 连接都未通过身份验证,同时也并非所有 RPC 服务器实际上都要求进行身份验证。
要想执行身份验证(RPC 中称之为“添加安全上下文”),客户端必须向绑定请求添加附加数据、协商身份验证协议(例如 NTLM 或 Kerberos),然后插入身份验证协议要求提供的附加元数据(例如用户名、域名等,参见图 2)。
发现研究目标
此类业务的一阶逻辑是从 API 的角度理解身份验证的表现形式。这可以通过创建绑定句柄,然后从客户端调用其中一个 RpcBindingSetAuthInfo* 函数来完成。如果查看这些函数的记录,就会发现一个名为 AuthenticationLevel的字段,它指明了身份验证所提供的安全防护级别。
利用身份验证进行安全防护时,不仅仅是验证用户是否存在且得到授权,还可预防发生篡改行为。身份验证级别各有不同,有的只是简单地验证连接成功 (RPC_C_AUTHN_LEVEL_CONNECT),有的则会对所有流量完全加密并签名以确保未发生任何篡改 (RPC_C_AUTHN_LEVEL_PKT_PRIVACY)。攻击者感兴趣的当然是前一种,因为该级别未对流量进行任何防御,这也就意味着有篡改的机会。
然而,事情并非如此简单。RPC 中继攻击并不是才出现的,所以 Windows 中的许多 RPC 客户端和服务器都已得到修补,并使用了最高级别的身份验证来确保中继攻击无法得逞。我们需要搜索整个操作系统,希望能找到一些由于某种原因而仍然不安全的旧代码块(图 3)。
WinReg——有文章可作的候选方案
正如所怀疑的那样,我们发现的潜在目标在 RPC 服务器和客户端总数中只占不到 5%;大多数设备都不再使用不安全的身份验证方法。但是,我们在 advapi32.dll中发现了一种有文章可作的候选方案。
advapi32.dll 是 Windows API 的一个核心组件,从其名称就能看出来,它会在 Windows 中实施许多“高级”逻辑。它能够从不同字段中导出超过 800 个函数,包括事件日志、加密、WMI 等等。
我们在研究中发现,在某些情况下,内部函数 BaseBindToMachine 会调用身份验证级别为 RPC_C_AUTHN_LEVEL_CONNECT 的 RpcBindingSetAuthInfoA ,而这正是我们想要的。在收到机器名称的 UNC 路径后,已导出(但并未记录)的函数 RegConnectRegistryExW 就会调用 BaseBindToMachine (图 4)。
通过分析 BaseBindToMachine,我们可以发现它实际上同时包含了对 RpcBindingSetAuthInfoW 和 RpcBindingSetAuthInfoA的调用。前者的使用十分安全,因为其身份验证级别为 RPC_C_AUTHN_LEVEL_PKT_PRIVACY;而后者使用的身份验证级别为 RPC_C_AUTHN_LEVEL_CONNECT,这意味着它可能会被中继,因为它并不会验证连接的真实性或完整性(图 5)。
我们只需要弄清楚为什么会发生两次相互冲突的调用。查看函数逻辑后可以发现,它有一个函数指针变量和一个函数数组(图 6)。这些函数将设置 RPC 绑定信息以使用特定的 RPC 传输协议;默认情况下,它会尝试使用 SMB 和命名管道。但如果失败,则会尝试通过 SPX、TCP/IP 和其他协议来绑定。
出于某些原因,如果它回退到 SMB 之外的其他任何协议,则会使用 RpcBindingSetAuthInfoA 将身份验证级别设置为“连接”,而这样并不安全。
回退到 TCP/IP 后就大有文章可作,这意味着我们可以利用不安全的身份验证方法,通过中间机器攻击来中继流量,而客户端对此将毫无察觉。虽然也可以使用其他传输协议,但它们已经相当过时了,在现代网络中可能已经很难找到,并且使用这些协议还可能会触发一些告警。TCP/IP 是一种更为常见的 RPC 传输协议,即使在红队设置中使用也很正常。
有一点务必要知道,那就是 BaseBindToMachine 和 RegConnectRegistryExW 都接受将一个标记用作防止回退行为的参数,但基本函数 RegConnectRegistryW 会调用 RegConnectRegistryExW ,其中并不存在该标记。
中继进程
中继相当简单,因为 NTLM 中继是一种很常见的技术。我们需要的大多数逻辑都已经在 Impacket的 ntlmrelayx 中得到实施,而这正是我们最常用的攻击方式。
构建 RPC 中继服务器
ntlmrelayx 缺乏的是 TCP/IP RPC 服务器,它仅实施了 SMB 服务器。因此,我们需要构建自己的中继服务器,以便利用它来拒绝 winreg 命名管道,从而触发回退到 TCP/IP 绑定函数。
我们需要实施三个关键点:
RPC 端点映射器
利用 NTLM 协议发起 RPC 绑定请求
NTLM 质询
RPC 端点映射器负责将 RPC 接口 UUID 转换为各自的端点,在 TCP/IP 传输的情况下,这些端点将是端口号。TCP 端点与 SMB 端点不同,后者是具有唯一性的命名管道,并且可以提前得知,而前者使用的是临时端口,因此需要另一层转换。
在这当中,关键因素是与 NTLM 相关的问题。RPC 绑定与 NTLM 的交互方式是在绑定请求期间发送 NTLM 协商消息,然后随绑定响应一起发送 NTLM 质询。最后,客户端必须发送另一条带有质询响应的消息,称为 AUTH3(图 7)。
如要进行中继,我们只需在自己的 RPC 服务器解析相应的消息。一旦在绑定消息中看到 NTLM 协商消息,我们就会与目标身份验证服务器建立自己的连接,同时请求通过 NTLM 进行身份验证。然后,我们只需捕获服务器发送给我们的质询,将其中继回到受害者处,再将响应中继回到服务器,即可获得我们自己的身份验证会话。
在这当中,我们只需要考虑通过哪台服务器来中继身份验证。
RPC 至 RPC 中继
直接的想法是中继到不同机器上的另一台 RPC 服务器,例如服务管理器或任务调度器,并通过这种方式来实现远程代码执行。但这样有一个问题,那就是没有人还会通过 RPC 进行不安全的身份验证,所以我们无法中继到任何高调的 RPC 服务器。(同样是因为这一原因,我们的研究目标也受到了限制。)
服务管理器和任务调度器 RPC 服务器都需要使用 RPC_C_AUTHN_LEVEL_PKT_PRIVACY,它会通过客户端的 NTLMv2 散列对整个流量进行加密,我们即使利用中继方法也无法得知这一信息。因此,我们需要从不同的角度来看待这一问题。
RPC 至 ADCS 中继
幸运的是,SpecterOps 的工作人员对 ADCS进行了大量研究,特别是 NTLM 至 ADCS 的中继。默认情况下,这一中继也是在 Impacket 中实施,所以我们要做的只是将通过身份验证的会话传送到 Impacket 的 HTTPAttack 模块,然后一切就都水到渠成了。
ADCS 的 HTTP Web 服务器不需要任何安全防护,因此很容易受到中继攻击。在通过 ADCS 身份验证后即可开始对其进行滥用,我们可以请求一份用户证书,随后只用这份证书就能完成身份验证,无需费尽心机地再次对身份验证进行中继(图 8)。
我们利用这份证书在域名控制器上通过了 LDAP 服务的身份验证,然后在遭受入侵的域中创建了一个永久性的新域名管理员角色(图 9)。
潜在影响
advapi 中的函数本身并没有什么用处,只有在被其他媒介利用时才会产生影响。如果只是快速查找 RegConnectRegistryExW 或 RegConnectRegistryExA 的导入,并不会暴露任何关于最新域名控制器的信息,但如果是搜索 RegConnectRegistryW ,就能发现许多潜在的候选方案,例如 certutil 以及 certsrv (AD CS)、EFS、DFS 等等。
检测
在所有 Windows 机器上,远程注册表服务均默认设置为未启用。可以通过以下 osquery 来检测其状态:
SELECT display_name, status, start_type, pid FROM services WHERE name='RemoteRegistry'
但是,这种方法并不能防御 CVE-2024-43532攻击,因为这是客户端上存在的问题。该查询的结果应该会列出组织内部的远程注册表实际应用场景,您在强化系统防护时可能需要加以考虑。
要想检测出有哪些客户端使用了任何易受攻击的 WinAPI,可以使用以下 YARA 规则:
import "pe"
rule winreg_client_import {
meta:
description = "Detect binaries that rely on RegConnectRegistry"
author = "Stiv Kupchik with Akamai Technologies"
condition:
pe.is_pe and (
pe.imports(pe.IMPORT_ANY, "advapi32.dll", "RegConnectRegistryA")
or pe.imports(pe.IMPORT_ANY, "advapi32.dll", "RegConnectRegistryW")
or pe.imports(pe.IMPORT_ANY, "advapi32.dll", "RegConnectRegistryExA")
or pe.imports(pe.IMPORT_ANY, "advapi32.dll", "RegConnectRegistryExW")
)
}
Akamai Guardicore Segmentation 用户还可以针对访问 RemoteRegistry 服务的流量创建策略规则(图 10)。
通过使用 Windows 事件跟踪 (ETW),还可以同时在通信的客户端和服务器端上监控 RPC 流量。我们已经在 2023 年黑帽大会 (Black Hat 2023) 的演示中详细讲述了这一主题,并在 相关的博文中进行了介绍。用户可以使用我们的 RPC 监测能力开源工具 来跟踪 RPC 调用,并筛选出 WinReg RPC 接口 UUID {338cd001-2244-31f1-aaaa-900038001003}。
结论
尽管 RPC 协议(以及 MS-RPC)在构建时考虑了安全性,但如果我们对各种 RPC 接口实施进行分析,就不难发现安全原则也在与时俱进。虽然大多数 RPC 服务器和客户端目前来说都是安全的,但偶尔还是会发现不安全实施所遗留下来的问题,只是程度不同而已。
有鉴于此,我们设法实现了 NTLM 中继,这是一类更适合以往的攻击。我们之所以这样做,只是为了证明网络防御必须尽可能完备无虞,因为您永远不会知道哪个年代久远的接口仍然暴露在风险之下或仍在运行。
披露时间表
2024 年 2 月 1 日——将漏洞披露给 MSRC
2024 年 4 月 25 日——报告关闭,问题已记录
2024 年 6 月 17 日——报告重启,提供了更完善的 PoC 和解释
2024 年 7 月 8 日——漏洞得到确认
2024 年 10 月 8 日——漏洞得到修补
2024 年 10 月 19 日——发布相关博文