0%

CVE-2024-43532-译

简述:

  1. Microsoft Remote Registry Service是Windows操作系统中的一个服务,允许远程用户通过网络访问和修改计算机上的注册表。
  2. 由于Microsoft Remote Registry客户端在SMB传输不可用的情况下回退到RPC认证时,切换到较旧的协议并采用弱认证级别,该级别无法验证通信的完整性或来源,攻击者可利用该缺陷,通过拦截 NTLM 身份验证握手并将其中继到其他服务,实现NTLM中继攻击,进而可能创建域管理员账户或 接管整个域

影响范围:

所有运行Windows Server版本的操作系统,包括2008到2022版及Window 10/11。

POC:

https://github.com/akamai/akamai-security-research/tree/main/PoCs/cve-2024-43532

Call and Register — Relay Attack on WinReg RPC Client

调用并注册——WinReg RPC 客户端的中继攻击

原文:https://www.akamai.com/blog/security-research/winreg-relay-vulnerability

Executive summary 执行摘要

  • Akamai 研究员 Stiv Kupchik 在 Microsoft 远程注册表客户端中发现了一个新的特权提升 (EoP) 漏洞 CVE-2024-43532,CVSS 评分为 8.8。
  • 该漏洞滥用 WinReg 客户端实现中的后备机制,如果 SMB 传输不可用,该机制将不安全地使用过时的传输协议。
  • 通过利用此漏洞,攻击者可以将客户端的 NTLM 身份验证详细信息中继到 Active Directory 证书服务 (ADCS),并请求用户证书以在域中进行进一步身份验证。我们在 GitHub存储库中提供了概念证明。
  • 该漏洞已于 2024 年 2 月负责任地向 Microsoft 安全资源中心披露,并作为 2024 年 10 月补丁星期二的一部分进行了修补。
  • 该漏洞影响所有未修补的 Windows 版本。
  • 我们的研究结果也在 No Hat 计算机安全会议上公布。

Introduction介绍

MS-RPC 是 Microsoft 对远程过程调用 (RPC) 协议和标准的实现。 RPC 是一种进程间通信机制,允许进程公开其他进程可以调用的功能。它是 Windows 操作系统的核心组件,多种服务都依赖于它 — 从服务管理器到 wininit 中的关闭

我们的团队对 MS-RPC 进行了大量研究,包括攻击性研究(我们发现了缓存攻击形式的大型攻击向量)和防御性研究(我们分析了其安全机制)。

这一次,我们想从不同的角度来看待 RPC。任何允许不同计算机之间进行通信和操作的协议都必须涉及用户身份验证,实际上 RPC 协议支持传递凭据和身份验证作为其绑定过程的一部分。然而,只要有身份验证,就有身份验证中继的潜力,因此我们开始寻找中继机会。

RPC, authentication, and what’s in betweenRPC、身份验证以及介于两者之间的内容

RPC sessions are handled by bindings . A client application connects to a server application, binds to the required RPC interface, and requests to run a specific function (Figure 1).RPC 会话由绑定处理。客户端应用程序连接到服务器应用程序,绑定到所需的 RPC 接口,并请求运行特定功能(图 1)。

Fig. 1: The basic RPC call process 图1:基本的RPC调用流程

The bind request and response can carry over multiple fields of data, as required for the connection. Normally, if there’s no authentication involved, the RPC binding is simply used to decide on the transfer syntax that will be used to encapsulate function parameters on subsequent calls.绑定请求和响应可以根据连接的需要携带多个数据字段。通常,如果不涉及身份验证,则 RPC 绑定仅用于决定将用于封装后续调用中的函数参数的传输语法。

What’s missing from this interaction? Authentication, of course! How can the server know the client’s identity and whether that client is allowed to perform the requested action? The answer is: It doesn’t, unless the client specifically added security context (authentication) to the binding. By default, all RPC connections are unauthenticated, and not all RPC servers actually require authentication.这种互动缺少什么?当然是认证啦!服务器如何知道客户端的身份以及是否允许该客户端执行请求的操作?答案是:不会,除非客户端专门向绑定添加了安全上下文(身份验证)。默认情况下,所有 RPC 连接都未经身份验证,并且并非所有 RPC 服务器实际上都需要身份验证。

To authenticate (or, in RPC terms, “add security context”), the client has to add additional data to the binding request, negotiate authentication protocol (for example, NTLM or Kerberos), and insert additional metadata required by the authentication protocol (like username, domain, etc.; Figure 2).为了进行身份验证(或者用 RPC 术语来说,“添加安全上下文”),客户端必须向绑定请求添加其他数据、协商身份验证协议(例如 NTLM 或 Kerberos),并插入身份验证协议所需的其他元数据(如用户名、域等;图 2)。

Fig 2: Authentication metadata added to RPC binding request图 2:添加到 RPC 绑定请求的身份验证元数据

Finding research targets寻找研究目标

The first order of business is understanding how authentication should look from an API standpoint. This is done by a call to one of the RpcBindingSetAuthInfo* functions from the client after creating a binding handle. If you look at the documentation of these functions, you’ll notice a field called AuthenticationLevel; it indicates the level of security the authentication provides.第一个任务是从 API 的角度理解身份验证应该如何看待。这是通过在创建绑定句柄后从客户端调用 RpcBindingSetAuthInfo* 函数之一来完成的。如果您查看这些函数的文档,您会注意到一个名为 AuthenticationLevel 的字段;它指示身份验证提供的安全级别。

Security with authentication is more than just verifying that the user exists and is authorized, it can also be used to prevent tampering. The authentication levels vary from simply verifying the connection succeeded (RPC_C_AUTHN_LEVEL_CONNECT) to fully encrypting and signing all the traffic to ensure nothing is tampered (RPC_C_AUTHN_LEVEL_PKT_PRIVACY). Of course, attackers would be interested in the former level, no defenses on the traffic, which means it’s tamperable.身份验证的安全性不仅仅是验证用户存在和授权,它还可以用于防止篡改。身份验证级别各不相同,从简单地验证连接是否成功 (RPC_C_AUTHN_LEVEL_CONNECT) 到完全加密和签署所有流量以确保任何内容不被篡改 (RPC_C_AUTHN_LEVEL_PKT_PRIVACY)。当然,攻击者会对前一个级别感兴趣,对流量没有防御,这意味着它是可篡改的。

Yet the story isn’t so simple. RPC relay attacks are not a new concept, so a lot of the RPC clients and servers in Windows were already patched to use the highest level of authentication to ensure relay attacks can’t succeed. We’ll need to scour the operating system in hopes of finding some older nuggets of code that are still insecure for some reason (Figure 3).然而故事并不那么简单。 RPC 中继攻击并不是一个新概念,因此 Windows 中的许多 RPC 客户端和服务器已经打了补丁,以使用最高级别的身份验证来确保中继攻击不会成功。我们需要搜索操作系统,希望找到一些由于某种原因仍然不安全的旧代码块(图 3)。

Fig. 3: Running an IDAPython script on the system32 folder of a Windows server to find instances of RPC insecure authentication; the script can be found in our GitHub repository图 3:在 Windows 服务器的 system32 文件夹上运行 IDAPython 脚本以查找 RPC 不安全身份验证的实例;该脚本可以在我们的 GitHub 存储库中找到

WinReg — A promising candidateWinReg——一个有前途的候选者

As suspected, the list of potential targets that we found included less than 5% of the total RPC servers and clients; most simply do not use insecure authentication anymore. But we found a promising candidate in advapi32.dll .正如所怀疑的,我们发现的潜在目标列表中不到 RPC 服务器和客户端总数的 5%;最简单的是不再使用不安全的身份验证。但我们在 advapi32.dll 中找到了一个有前途的候选者。

advapi32.dll is a core component of Windows API, and implements a lot of the “advanced” logic in Windows (as its name suggests). It exports more than 800 functions from various fields: event logging, encryption, WMI, and more.advapi32.dll是Windows API的核心组件,实现了Windows中的许多“高级”逻辑(顾名思义)。它导出来自各个领域的 800 多个功能:事件记录、加密、WMI 等。

For our purposes, we’ve found that, on some occasions, the internal function BaseBindToMachine calls RpcBindingSetAuthInfoA with an authentication level of RPC_C_AUTHN_LEVEL_CONNECT, which is what we want. BaseBindToMachine is called by the exported (though undocumented) function RegConnectRegistryExW (Figure 4), if it receives a UNC path for a machine name.出于我们的目的,我们发现,在某些情况下,内部函数 BaseBindToMachine 会使用 RPC_C_AUTHN_LEVEL_CONNECT 的身份验证级别调用 RpcBindingSetAuthInfoA,这正是我们想要的。如果 BaseBindToMachine 收到计算机名称的 UNC 路径,则由导出的(尽管未记录的)函数 RegConnectRegistryExW(图 4)调用它。

Fig. 4: The Rust documentation for RegConnectRegistryExW, as it is undocumented in MSDN图 4:RegConnectRegistryExW 的 Rust 文档,因为 MSDN 中没有记录它

By looking at BaseBindToMachine , we can see that it actually contains calls to both RpcBindingSetAuthInfoW and RpcBindingSetAuthInfoA . The former is used securely, with authentication level RPC_C_AUTHN_LEVEL_PKT_PRIVACY, while the latter uses authentication level RPC_C_AUTHN_LEVEL_CONNECT, which can be relayed as it doesn’t verify authenticity or integrity of the connection (Figure 5).通过查看BaseBindToMachine,我们可以看到它实际上包含对RpcBindingSetAuthInfoW和RpcBindingSetAuthInfoA的调用。前者使用安全,身份验证级别为 RPC_C_AUTHN_LEVEL_PKT_PRIVACY,而后者使用身份验证级别 RPC_C_AUTHN_LEVEL_CONNECT,可以中继,因为它不验证连接的真实性或完整性(图 5)。

Fig. 5: The two calls to set authentication info图 5:设置身份验证信息的两次调用

We just need to figure out why there are two conflicting calls. Looking at the function logic, we can see that it has a function pointer variable and an array of functions (Figure 6). Those functions set the RPC binding info to use a specific RPC transport; by default it attempts to use SMB and named pipes — but, if it fails, it attempts to bind over SPX, TCP/IP, and others.我们只需要弄清楚为什么会有两个相互冲突的调用。查看函数逻辑,我们可以看到它有一个函数指针变量和一个函数数组(图6)。这些函数设置 RPC 绑定信息以使用特定的 RPC 传输;默认情况下,它会尝试使用 SMB 和命名管道,但如果失败,它会尝试通过 SPX、TCP/IP 等进行绑定。

For some reason, once it falls back to any other protocol besides SMB, it uses RpcBindingSetAuthInfoA to set the authentication level to Connect, which is insecure.由于某种原因,一旦它回退到 SMB 之外的任何其他协议,它就会使用 RpcBindingSetAuthInfoA 将身份验证级别设置为 Connect,这是不安全的。

Fig. 6: Function pointers and the function array inside BaseBindToMachine图 6:BaseBindToMachine 内的函数指针和函数数组

The fallback to TCP/IP is quite promising, as it means we can use the insecure authentication method to relay the traffic using a machine-in-the-middle attack without the client noticing. While it is possible with the other transport protocols as well, they are quite obsolete, so it might be unrealistic to find them in a modern network (and using them might trigger some alarm bells). TCP/IP is a lot more common as an RPC transport, so it should be okay even in a red team setting.TCP/IP 的回退是非常有前途的,因为这意味着我们可以使用不安全的身份验证方法,通过中间机器攻击来中继流量,而无需客户端注意。虽然其他传输协议也是可能的,但它们已经过时了,因此在现代网络中找到它们可能是不现实的(并且使用它们可能会触发一些警钟)。 TCP/IP 作为 RPC 传输更为常见,因此即使在红队环境中也应该没问题。

It is important to know that both BaseBindToMachine and *RegConnectRegistryExW *accept a flag as an argument that prevents the fallback behavior, but the basic function RegConnectRegistryW calls *RegConnectRegistryExW *without that flag present.重要的是要知道 BaseBindToMachine 和 RegConnectRegistryExW 都接受一个标志作为防止回退行为的参数,但基本函数 RegConnectRegistryW 在没有该标志的情况下调用 RegConnectRegistryExW。

The relay process中继过程

Relaying is quite simple since NTLM relay is a common technique. Most of the logic we need is already implemented in Impacket’s ntlmrelayx, which is what we’ll use the most.中继非常简单,因为 NTLM 中继是一种常用技术。我们需要的大部分逻辑已经在 Impacket 的 ntlmrelayx 中实现,这也是我们最常用的。

Building the RPC relay server构建RPC中继服务器

What ntlmrelayx lacks is a TCP/IP RPC server, as it implemented only an SMB server. As such, we’ll need to build our own relay server, which rejects the winreg named pipe to trigger a fallback to the TCP/IP binding function.ntlmrelayx 缺少的是 TCP/IP RPC 服务器,因为它只实现了 SMB 服务器。因此,我们需要构建自己的中继服务器,该服务器拒绝 winreg 命名管道以触发 TCP/IP 绑定功能的回退。

There are three critical points we need to implement:我们需要实施三个关键点:

  1. The RPC endpoint mapperRPC端点映射器
  2. The RPC bind request with NTLM与 NTLM 的 RPC 绑定请求
  3. The NTLM challengeNTLM 挑战

The RPC endpoint mapper is responsible for translating RPC interface UUIDs into their respective endpoints, which in the case of the TCP/IP transport, would be a port number. Unlike SMB, where the endpoints are named pipes that are unique and can be known in advance, the TCP endpoints use ephemeral ports, so another layer of translation is necessary.RPC 端点映射器负责将 RPC 接口 UUID 转换为其各自的端点,在 TCP/IP 传输的情况下,这将是端口号。与 SMB 不同,SMB 的端点是唯一且可以提前知道的命名管道,而 TCP 端点使用临时端口,因此需要另一层转换。

The critical part is the NTLM-related issues. The way RPC binding works with NTLM is that an NTLM negotiation message is sent during the bind request, then an NTLM challenge is sent with the bind response. Finally, the client has to send another message, called AUTH3, with the challenge response (Figure 7).关键部分是NTLM相关问题。 RPC 绑定与 NTLM 结合使用的方式是,在绑定请求期间发送 NTLM 协商消息,然后随绑定响应一起发送 NTLM 质询。最后,客户端必须发送另一条名为 AUTH3 的消息以及质询响应(图 7)。

Fig. 7: NTLM authentication over RPC binding图 7:通过 RPC 绑定进行 NTLM 身份验证

To relay, we just have to parse the corresponding messages in our RPC server. Once we see an NTLM negotiate message in a bind message, we open our own connection to our target authentication server and also request to authenticate via NTLM. Then, we just have to capture the challenge the server sends us, relay it back to the victim, and relay the response back to the server to get our own authenticated session.要进行中继,我们只需在 RPC 服务器中解析相应的消息即可。一旦我们在绑定消息中看到 NTLM 协商消息,我们就会打开与目标身份验证服务器的连接,并请求通过 NTLM 进行身份验证。然后,我们只需捕获服务器发送给我们的质询,将其转发回受害者,并将响应转发回服务器以获得我们自己的经过身份验证的会话。

All we need to consider is which server we want to relay the authentication.我们需要考虑的是我们想要中继身份验证的服务器。

RPC to RPC relayRPC 到 RPC 中继

The immediate thought would be to relay to another RPC server on a different machine, like the service manager or the task scheduler, and achieve a remote code execution that way. The issue with this, however, is that no one uses insecure authentication via RPC anymore, so we can’t relay to any high-profile RPC server. (This lack of insecure authentication via RPC is the same reason we had limited research targets.)最直接的想法是中继到不同机器上的另一个 RPC 服务器,例如服务管理器或任务调度程序,并以这种方式实现远程代码执行。然而,这样做的问题是,没有人再通过 RPC 使用不安全的身份验证,因此我们无法中继到任何高调的 RPC 服务器。 (缺乏通过 RPC 进行的不安全身份验证与我们研究目标有限的原因相同。)

Both the service manager and the task scheduler RPC servers require RPC_C_AUTHN_LEVEL_PKT_PRIVACY, which encrypts the entirety of the traffic with the client’s NTLMv2 hash, which we don’t know even with the relay. Instead, we have to look at a different angle.服务管理器和任务调度程序 RPC 服务器都需要 RPC_C_AUTHN_LEVEL_PKT_PRIVACY,它使用客户端的 NTLMv2 哈希加密整个流量,即使使用中继,我们也不知道该哈希。相反,我们必须换个角度来看。

RPC to ADCSRPC 到 ADCS

Luckily, the guys at SpecterOps have done a lot of work on ADCS; specifically on NTLM relay to ADCS. This is also implemented in Impacket by default, so all we have to do is pass our authenticated session to Impacket’s HTTPAttack module and let the magic happen by itself.幸运的是,SpectreOps 的人员在 ADCS 方面做了很多工作;特别是关于 ADCS 的 NTLM 中继。默认情况下,这也在 Impacket 中实现,因此我们所要做的就是将经过身份验证的会话传递给 Impacket 的 HTTPAttack 模块,然后让魔法自行发生。

ADCS’s HTTP web server doesn’t require any security and is vulnerable to relay attacks. By abusing that, once we authenticate to it, we can request a user certificate, which we can then use by itself to authenticate, without having to go through the trouble of relaying the authentication again (Figure 8).ADCS 的 HTTP Web 服务器不需要任何安全性,并且容易受到中继攻击。通过滥用这一点,一旦我们对其进行身份验证,我们就可以请求用户证书,然后我们可以使用该证书本身进行身份验证,而无需再次经历中继身份验证的麻烦(图 8)。

Fig 8: Requesting and receiving a certificate from ADCS following an NTLM relay attack图8:NTLM继电器攻击后请求并收到ADC的证书

We used this certificate to authenticate to the LDAP service on the domain controller and create a persistent new domain admin in the compromised domain (Figure 9).我们使用此证书对域控制器上的 LDAP 服务进行身份验证,并在受感染的域中创建持久的新域管理员(图 9)。

Fig 9: Creating a new domain admin using an LDAP shell图 9:使用 LDAP shell 创建新的域管理员

Potential impact潜在影响

A function in advapi isn’t interesting by itself, it is only impactful if something else is using it. A quick lookup of imports of RegConnectRegistryExW or RegConnectRegistryExA doesn’t show anything on an up-to-date domain controller, but a search for RegConnectRegistryW uncovers a lot of potential candidates, like certutil and certsrv (AD CS), EFS, DFS, and more.advapi 中的函数本身并不有趣,只有当其他东西使用它时它才会产生影响。快速查找 RegConnectRegistryExW 或 RegConnectRegistryExA 的导入不会在最新的域控制器上显示任何内容,但搜索 RegConnectRegistryW 会发现许多潜在的候选者,例如 certutil 和 certsrv (AD CS)、EFS、DFS 和更多的。

Detection检测

The Remote Registry service isn’t enabled by default on all Windows machines. It is possible to detect it’s status using the following osquery:默认情况下,并非所有 Windows 计算机上都启用远程注册表服务。可以使用以下 osquery 检测其状态:

1
SELECT display_name, status, start_type, pid FROM services WHERE name='RemoteRegistry'

This, however, doesn’t protect from CVE-2024-43532, as it is a client issue. The results of the query should raise actual use cases for the Remote Registry in the organization that you might need to account for when hardening your systems.然而,这并不能防止 CVE-2024-43532,因为这是一个客户端问题。查询结果应提出组织中远程注册表的实际用例,您在强化系统时可能需要考虑这些用例。

To detect clients that use any of the vulnerable WinAPI, you can use the following YARA rule:要检测使用任何易受攻击的 WinAPI 的客户端,您可以使用以下 YARA 规则:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 users can also create policy rules for traffic coming into the RemoteRegistry service (Figure 10).Akamai Guardicore Segmentation 用户还可以为进入 RemoteRegistry 服务的流量创建策略规则(图 10)。

Fig. 10: Segmentation policy rule to alert on traffic to the RemoteRegistry service图 10:针对 RemoteRegistry 服务的流量发出警报的分段策略规则

It is also possible to use Event Tracing for Windows (ETW) to monitor RPC traffic on both client and server sides of the communication. We’ve elaborated on this topic in our presentation at Black Hat 2023 and its accompanying blog post. Users can use our RPC visibility open-source tool to track RPC calls and filter for the WinReg RPC interface UUID {338cd001-2244-31f1-aaaa-900038001003} .还可以使用 Windows 事件跟踪 (ETW) 来监视通信客户端和服务器端的 RPC 流量。我们在 Black Hat 2023 的演讲及其随附的博客文章中详细阐述了这个主题。用户可以使用我们的 RPC 可见性开源工具来跟踪 RPC 调用并过滤 WinReg RPC 接口 UUID {338cd001-2244-31f1-aaaa-900038001003}。

Conclusion结论

Although the RPC protocol — and MS-RPC — were built with security in mind, we can clearly see the evolution of security principles over time by analyzing various RPC interface implementations. While most RPC servers and clients are secure nowadays, it is possible, from time to time, to uncover relics of insecure implementation to varying degrees.尽管 RPC 协议和 MS-RPC 在构建时就考虑到了安全性,但通过分析各种 RPC 接口实现,我们可以清楚地看到安全原则随时间的演变。虽然现在大多数 RPC 服务器和客户端都是安全的,但有时仍可能会不同程度地发现不安全实现的遗留问题。

In this case, we managed to achieve NTLM relay, which is a class of attacks that better belongs to the past. This just proves that network defenses must be as thorough as possible because you never know which ancient interface is still exposed or running.在这种情况下,我们成功实现了 NTLM 中继,这是一类更好地属于过去的攻击。这恰恰证明网络防御必须尽可能彻底,因为你永远不知道哪个古老的接口仍然暴露或运行。