mTLS: When certificate authentication is done wrong

渗透技巧 8个月前 admin
236 0 0

mTLS: When certificate authentication is done wrong

Although X.509 certificates have been here for a while, they have become more popular for client authentication in zero-trust networks in recent years. Mutual TLS, or authentication based on X.509 certificates in general, brings advantages compared to passwords or tokens, but you get increased complexity in return.
尽管 X.509 证书已经存在了一段时间,但近年来它们在零信任网络中的客户端身份验证中变得越来越流行。与密码或令牌相比,相互 TLS 或通常基于 X.509 证书的身份验证具有优势,但作为回报,复杂性会增加。

In this post, I’ll deep dive into some interesting attacks on mTLS authentication. We won’t bother you with heavy crypto stuff, but instead we’ll have a look at implementation vulnerabilities and how developers can make their mTLS systems vulnerable to user impersonation, privilege escalation, and information leakages.
在这篇文章中,我将深入探讨一些关于mTLS身份验证的有趣攻击。我们不会用繁重的加密内容来打扰您,而是我们将研究实施漏洞以及开发人员如何使其 mTLS 系统容易受到用户冒充、权限提升和信息泄露的影响。

We will present some CVEs we found in popular open-source identity servers and ways to exploit them. Finally, we’ll explain how these vulnerabilities can be spotted in source code and how to fix them.
我们将介绍我们在流行的开源身份服务器中发现的一些 CVE 以及利用它们的方法。最后,我们将解释如何在源代码中发现这些漏洞以及如何修复它们。

This blog post is based on work that I recently presented at Black Hat USA and DEF CON.
这篇博文基于我最近在Black Hat USA和DEF CON上展示的工作。

Introduction: What is mutual TLS?
简介:什么是双向 TLS?

Website certificates are a very widely recognized technology, even to people who don’t work in the tech industry, thanks to the padlock icon used by web browsers. Whenever we connect to Gmail or GitHub, our browser checks the certificate provided by the server to make sure it’s truly the service we want to talk to. Fewer people know that the same technology can be used to authenticate clients: the TLS protocol is also designed to be able to verify the client using public and private key cryptography.
网站证书是一项非常广泛认可的技术,即使对于不在科技行业工作的人来说也是如此,这要归功于网络浏览器使用的挂锁图标。每当我们连接到Gmail或GitHub时,我们的浏览器都会检查服务器提供的证书,以确保它确实是我们想要与之通信的服务。很少有人知道相同的技术可用于对客户端进行身份验证:TLS 协议还设计为能够使用公钥和私钥加密来验证客户端。

It happens on the handshake level, even before any application data is transmitted:
它发生在握手级别,甚至在传输任何应用程序数据之前:

mTLS: When certificate authentication is done wrong

If configured to do so, a server can ask a client to provide a security certificate in the X.509 format. This certificate is just a blob of binary data that contain information about the client, such as its name, public key, issuer, and other fields:
如果配置为这样做,服务器可以要求客户端提供 X.509 格式的安全证书。此证书只是包含有关客户端的信息(如其名称、公钥、颁发者和其他字段)的二进制数据 blob:

$ openssl x509 -text -in client.crt
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            d6:2a:25:e3:89:22:4d:1b
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=localhost            //used to locate issuers certificate
        Validity
            Not Before: Jun 13 14:34:28 2023 GMT
            Not After : Jul 13 14:34:28 2023 GMT
        Subject: CN=client          //aka "user name"
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:9c:7c:b4:e5:e9:3d:c1:70:9c:9d:18:2f:e8:a0:

The server checks that this certificate is signed by one of the trusted authorities. This is a bit similar to checking the signature of a JWT token. Next, the client sends a “Certificate verify” message encrypted with the private key, so that the server can verify that the client actually has the private key.
服务器检查此证书是否由其中一个受信任的颁发机构签名。这有点类似于检查 JWT 令牌的签名。接下来,客户端发送使用私钥加密的“证书验证”消息,以便服务器可以验证客户端是否确实拥有私钥。

How certificates are validated
如何验证证书

“Certificate validation” commonly refers to the PKIX certificate validation process defined in RFC 5280.
“证书验证”通常是指 RFC 5280 中定义的 PKIX 证书验证过程。

In short, in order to validate the certificate, the server constructs a certification path (also known as a certificate chain) from the target certificate to a trust anchor. The trust anchor is a self-signed root certificate that is inherently trusted by the validator. The end entity certificate is often signed by an intermediate CA, which is also signed by another intermediate certificate or directly by a trust anchor.
简而言之,为了验证证书,服务器构造从目标证书到信任锚的证书路径(也称为证书链)。信任锚是验证者固有信任的自签名根证书。最终实体证书通常由中间 CA 签名,中间 CA 也由另一个中间证书或直接由信任锚签名。

mTLS: When certificate authentication is done wrong

Then, for each certificate in the chain, the validator checks the signature, validity period, allowed algorithms and key lengths, key usage, and other properties. There are also a number of optional certificate extensions: if they are included in the certificate, they can be checked as well. This process is quite complicated, so every language or library implements it differently.
然后,对于链中的每个证书,验证器检查签名、有效期、允许的算法和密钥长度、密钥用法和其他属性。还有许多可选的证书扩展:如果它们包含在证书中,也可以检查它们。这个过程非常复杂,因此每种语言或库都以不同的方式实现它。

Note: in my research I mostly looked at how mTLS is implemented in applications written in Java, but it is likely that the ideas and attacks below apply to other languages as well.
注意:在我的研究中,我主要研究如何在用Java编写的应用程序中实现mTLS,但下面的思想和攻击可能也适用于其他语言。

mTLS in a Java web application, an example
Java Web 应用程序中的 mTLS,示例

Let’s see how to use mTLS in a Java web application. The bare minimum configuration is to enable it in the application settings and specify the location of all trusted root certificates, like this:
让我们看看如何在Java Web应用程序中使用mTLS。最低配置是在应用程序设置中启用它并指定所有受信任根证书的位置,如下所示:

$ cat application.properties

…
server.ssl.client-auth=need
server.ssl.trust-store=/etc/spring/server-truststore.p12
server.ssl.trust-store-password=changeit

From the client, such as curl, you need to specify which certificate is sent to the server. The rest of the application code, such as request mappings, is exactly the same as for a normal web application.
从客户端(如 curl)需要指定发送到服务器的证书。其余的应用程序代码(如请求映射)与普通 Web 应用程序完全相同。

$ curl -k -v –cert client.pem http://localhost/hello

This setup works for very simple mTLS configurations, when there is only a single root certificate, and all client certificates are signed by it. You can find this example in various articles on the web and it’s quite secure due to its simplicity. Let’s quickly break down its pros and cons.
此设置适用于非常简单的 mTLS 配置,当只有一个根证书并且所有客户端证书都由它签名时。您可以在网络上的各种文章中找到此示例,并且由于其简单性,它非常安全。让我们快速分解它的优缺点。

Pros: 优点:

  • Speed: Authorization happens only during TLS handshake, all subsequent “keep-alive” HTTP requests are considered authenticated, saving CPU time.
    速度:授权仅在TLS握手期间发生,所有后续的“保持活动”HTTP请求都被视为经过身份验证,从而节省了CPU时间。
  • Storage: Similar to JWT, the server does not store all client certificates, only the root certificate.
    存储:与 JWT 类似,服务器不存储所有客户端证书,仅存储根证书。

Cons: 缺点:

  • No granular control: if mTLS is enabled, all requests have to be authenticated, even to /static/style.css.
    无精细控制:如果启用了 mTLS,则必须对所有请求进行身份验证,甚至对 /static/style.css .
  • Any certificate signed by a trusted CA can be used to access this HTTP service. Even if the certificate is issued for another purpose, it still can potentially be used for TLS authentication.
    由受信任的 CA 签名的任何证书都可用于访问此 HTTP 服务。即使证书是为其他目的颁发的,它仍然可以用于 TLS 身份验证。
  • No host verification by default: client certificates can be accepted from any IP.
    默认情况下不进行主机验证:可以接受来自任何 IP 的客户端证书。
  • Certificate issuance process needs to be implemented separately.
    证书颁发过程需要单独实施。
  • Certificates expire, so need to be rotated frequently.
    证书过期,因此需要经常轮换。

As you can see, this approach brings some advantages and disadvantages compared to traditional authentication methods, such as password or tokens.
如您所见,与传统的身份验证方法(例如密码或令牌)相比,此方法带来了一些优点和缺点。

Previous attacks 以前的攻击

Before we dive into the attack section, I’ll briefly mention some previous well-known attacks on certificate parsing and validation:
在我们深入攻击部分之前,我将简要提及一些以前众所周知的证书解析和验证攻击:

  • Obviously, the security of the authentication system depends on the strength of the signature. If we can somehow forge the content of the certificate, but keep the same signature, we can completely break the authentication process.
    显然,认证系统的安全性取决于签名的强度。如果我们可以以某种方式伪造证书的内容,但保留相同的签名,我们可以完全破坏身份验证过程。
  • Since the X.509 format is quite complex, just parsing these data structures can lead to buffer and heap overflows.
    由于 X.509 格式非常复杂,因此仅分析这些数据结构就可能导致缓冲区和堆溢出。
  • Lack of basic constraints checking. The end-entity certificates should not be used to sign additional certificates.
    缺乏基本的约束检查。不应使用最终实体证书来签署其他证书。

My approach 我的方法

In Java, most of these attacks are already mitigated in APIs provided by the JDK. Weak algorithms are intentionally not allowed. Fuzzing of certificate parsing in Java also did not look productive to me, as the vast majority of PKIX code is implemented in memory-safe Java, instead of using native libraries. I had to take a different approach, so I decided to have a deep look at how mTLS is used from the source code perspective. Since the certificate validation process is quite complex, I suspected that someone might implement it in a weird way. After several weeks, it yielded me some interesting vulnerabilities in popular open source projects.
在Java中,大多数这些攻击已经在JDK提供的API中得到了缓解。故意不允许使用弱算法。对我来说,Java 中的证书解析模糊测试看起来也没有效率,因为绝大多数 PKIX 代码都是在内存安全的 Java 中实现的,而不是使用本机库。我不得不采取不同的方法,所以我决定从源代码的角度深入研究如何使用mTLS。由于证书验证过程非常复杂,我怀疑有人可能会以奇怪的方式实现它。几周后,它在流行的开源项目中发现了一些有趣的漏洞。

So, let’s move on to the attack’s section.
因此,让我们继续攻击部分。

Chapter 1: Improper certificate extraction
第 1 章:证书提取不当

In real-life applications, developers often need to access the certificate presented during the TLS handshake. For example, they might need it for authorization purposes, such as checking the current username. In Java, there are two common ways how to access it:
在实际应用中,开发人员通常需要访问 TLS 握手期间提供的证书。例如,他们可能需要它进行授权,例如检查当前用户名。在 Java 中,有两种常见的访问方式:

X509Certificate[] certificates = sslSession.getPeerCertificates();  

// another way
X509Certificate[] certificates = request.getAttribute("javax.servlet.request.X509Certificate");

Interestingly, this API returns an array of certificates presented by the client, not a single one. Why? Perhaps because TLS specification defines that clients may send a full chain of certificates, from end-entity to the root CA.
有趣的是,此 API 返回客户端提供的证书数组,而不是单个证书。为什么?也许是因为 TLS 规范定义了客户端可以发送从最终实体到根 CA 的完整证书链。

So, I decided to take a look at how different applications use this API. The most common approach I’ve seen is to take only the first certificate from the array and consider it as the client certificate. This is correct, as mTLS RFC explicitly says that the sender’s certificate MUST come first in the list.
因此,我决定看看不同的应用程序如何使用这个 API。我见过的最常见的方法是仅从阵列中获取第一个证书,并将其视为客户端证书。这是正确的,因为 mTLS RFC 明确表示发件人的证书必须在列表中排在第一位。

//way 1 is good
String user = certificates[0].getSubjectX500Principal().getName();

At the same time, I discovered some rare cases when applications disregard this rule and iterate over the array trying to find a certificate that matches some criteria.
同时,我发现了一些罕见的情况,即应用程序忽略此规则并遍历数组以查找与某些条件匹配的证书。

//way 2 is dangerous
for (X509Certificate cert : certificates) {
   if (isClientCertificate(cert)) {
      user = cert.getSubjectX500Principal().getName();
   }
}

This is dangerous, as the underlying TLS library in Java only verifies the first certificate in the list. Moreover, it does not require the chain to be sent in a strict order.

Example: CVE-2023-2422 improper certificate validation in KeyCloak

One of these examples was a vulnerability I discovered in Keycloak. Keycloak is a popular authorization server that supports OAuth, SAML, and other authorization methods, as well as mutual TLS.

Keycloak iterates over all certificates in the array, searching for the one that matches the client_id form parameter. As soon as it finds a matching certificate, it implicitly trusts it, assuming that its signature has already been checked during the TLS handshake:

X509Certificate[] certs = null;
ClientModel client = null;
try { 
    certs = provider.getCertificateChain(context.getHttpRequest());
    String client_id = null;
    ...
    if (formData != null) {
        client_id = formData.getFirst(OAuth2Constants.CLIENT_ID);
    }
    …
    matchedCertificate = Arrays.stream(certs)
        .map(certificate -> certificate.getSubjectDN().getName())
        .filter(subjectdn -> subjectDNPattern.matcher(subjectdn).matches())
        .findFirst();

In reality, a client can send as many certificates as they want, and the server only verifies the first one.

A potential attacker can exploit this behavior to authenticate under a different username. It is possible to send a list of certificates, where the first one contains one username and is properly chained to a root CA. But the last certificate in the array might be self signed and belong to a different user. The client does not even need to provide a valid private key for it.

mTLS: When certificate authentication is done wrong

Speaking about the exploitation, there are a number of endpoints in Keycloak that support mTLS authentication, but we need one that does not require any additional factors, such as tokens or secrets. “client-management/register-node” is a good example, as it mutates the user’s data. We can normally use this api with mTLS in the following way:

$ cat client1.crt client1.key > chain1.pem
$ curl --tlsv1.2 --tls-max 1.2 --cert chain1.pem -v -i -s -k "https://127.0.0.1:8443/realms/master/clients-managements/register-node?client_id=client1" -d "client_cluster_host=http://127.0.0.1:1213/"

To demonstrate the vulnerability, we generate a new self signed certificate using openssl and add it to the end of the array.
为了演示该漏洞,我们使用 openssl 生成一个新的自签名证书,并将其添加到数组的末尾。

$ openssl req -newkey rsa:2048 -nodes -x509 -subj /CN=client2 -out client2-fake.crt
$ cat client1.crt client1.key client2-fake.crt client1.key > chain2.pem
$ curl --tlsv1.2 --tls-max 1.2 --cert chain2.pem -v -i -s -k "https://127.0.0.1:8443/realms/master/clients-managements/register-node?client_id=client2" -d "client_cluster_host=http://127.0.0.1:1213/"

When we send the second curl request, Keycloak performs this action on behalf of the user specified in client2-fake.crt, instead of client1.crt. Therefore, we can mutate data on the server for any client that supports mTLS.
当我们发送第二个 curl 请求时,Keycloak 代表 中 client2-fake.crt 指定的用户执行此操作,而不是 client1.crt .因此,我们可以为任何支持 mTLS 的客户端更改服务器上的数据。

How to fix that? Easy: just use the first certificate from the array. That’s exactly how Keycloak patched this vulnerability. This CVE is a good example of how developers provide methods and interfaces that can be misunderstood or used incorrectly.
如何解决?简单:只需使用阵列中的第一个证书。这正是Keycloak修补此漏洞的方式。此 CVE 是开发人员如何提供可能被误解或错误使用的方法和接口的一个很好的例子。

Passing certificate as a header
将证书作为标头传递

Another common scenario for mTLS deployments is when the TLS connection is terminated on a reverse proxy. In this case, the reverse proxy often checks the certificate and forwards it to a backend server as an additional header. Here is a typical nginx configuration to enable mTLS:
mTLS 部署的另一种常见情况是 TLS 连接在反向代理上终止。在这种情况下,反向代理通常会检查证书并将其作为附加标头转发到后端服务器。以下是启用mTLS的典型nginx配置:

$ cat nginx.conf

http {
    server {
        server_name example.com;
        listen 443 ssl;
        …
        ssl_client_certificate /etc/nginx/ca.pem;
        ssl_verify_client on;

        location / {
            proxy_pass http://host.internal:80;
            proxy_set_header ssl-client-cert $ssl_client_cert;
        }
    }

I’ve seen a number of systems like that, and in most cases the backend servers behind nginx do not perform additional validation, just trusting the reverse proxy. This behavior is not directly exploitable, but it’s not ideal either. Why? Well, first of all, it means that any server in the local network can make a request with this header, so this network segment needs to be carefully isolated from any traffic coming from outside. Additionally, if the backend or reverse proxy is affected by request smuggling or header injection, its exploitation becomes trivial. Over the past few years, we’ve seen a lot of request and header smuggling vulnerabilities, including the latest CVEs in Netty and Nodejs. Be careful when implementing these scenarios and check the certificate’s signature on all servers if possible.
我见过许多这样的系统,在大多数情况下,nginx后面的后端服务器不执行额外的验证,只是信任反向代理。此行为不可直接利用,但也不理想。为什么?嗯,首先,这意味着本地网络中的任何服务器都可以使用此标头发出请求,因此需要仔细地将此网段与来自外部的任何流量隔离开来。此外,如果后端或反向代理受到请求走私或标头注入的影响,则其利用变得微不足道。在过去的几年中,我们看到了很多请求和标头走私漏洞,包括Netty和Nodejs中的最新CVE。实现这些方案时要小心,如果可能,请在所有服务器上检查证书的签名。

Chapter 2: “Follow the chain, where does it lead you?”
第2章 “跟着链子走,它把你引向何方?

mTLS: When certificate authentication is done wrong

In large systems, servers may not store all root and intermediate certificates locally, but use external storage instead. RFC 4387 explains the concept of a certificate store: an interface you can use to lazily access certificates during chain validation. These stores are implemented over different protocols, such as HTTP, LDAP, FTP, or SQL queries.
在大型系统中,服务器可能不会在本地存储所有根证书和中间证书,而是使用外部存储。RFC 4387 解释了证书存储的概念:可用于在链验证期间延迟访问证书的接口。这些存储通过不同的协议实现,例如 HTTP、LDAP、FTP 或 SQL 查询。

RFC 3280 defines some X.509 certificate extensions that can contain information about where to find the issuer and CA certificates. For instance, the Authority Information Access (AIA) extension contains a URL pointing to the Issuer’s certificate. If this extension is used for validation, there is a high chance that you can exploit it to perform an SSRF attack. Also, Subject, Issuer, Serial, and their alternative names can be used to construct SQL or LDAP queries, creating opportunities for injection attacks.
RFC 3280 定义了一些 X.509 证书扩展,这些扩展可以包含有关在何处查找颁发者和 CA 证书的信息。例如,颁发机构信息访问 (AIA) 扩展包含指向颁发者证书的 URL。如果此扩展用于验证,则很有可能利用它来执行 SSRF 攻击。此外,主题、颁发者、串行及其备用名称可用于构造 SQL 或 LDAP 查询,从而为注入攻击创造机会。

mTLS: When certificate authentication is done wrong

When certificate stores are in use, you should think of these values as “untrusted user input” or “Insertion points,” similar to those we have in Burp Suite’s Intruder. And what attackers will really love is that all of these values can be used in queries before the signature is checked.
使用证书存储时,应将这些值视为“不受信任的用户输入”或“插入点”,类似于我们在Burp Suite的入侵者中具有的值。攻击者真正喜欢的是,在检查签名之前,所有这些值都可以在查询中使用。

Example: CVE-2023-33201 LDAP injection in Bouncy Castle
示例:充气城堡中的 CVE-2023-33201 LDAP 注入

To demonstrate an example of this vulnerability, we’ll use LDAPCertStore from the Bouncy Castle library. Bouncy Castle is one of the most popular libraries for certificate validation in Java. Here is an example of how you can use this store to build and validate a certificate chain.
为了演示此漏洞的示例,我们将使用Bouncy Castle库中的LDAPCertStore。Bouncy Castle是Java中最受欢迎的证书验证库之一。下面是如何使用此存储生成和验证证书链的示例。

PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(keystore, selector);

//setup additional LDAP store
X509LDAPCertStoreParameters CertStoreParameters = new X509LDAPCertStoreParameters.Builder("ldap://127.0.0.1:1389", "CN=certificates").build();
CertStore certStore = CertStore.getInstance("LDAP", CertStoreParameters, "BC");
pkixParams.addCertStore(certStore);

// Build and verify the certification chain
try {
   CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC");
   PKIXCertPathBuilderResult result =
           (PKIXCertPathBuilderResult) builder.build(pkixParams);

Under the hood, Bouncy Castle uses the Subject field from the certificate to build an LDAP query. The Subject field is inserted in the filter, without—you guessed it—any escaping.
在后台,Bouncy Castle 使用证书中的“主题”字段来构建 LDAP 查询。“主题”字段插入到筛选器中,没有任何转义(您猜对了)。

mTLS: When certificate authentication is done wrong

So, if the Subject contains any special characters, it can change the syntax of the query. In most cases, this can be exploited as a blind ldap query injection. Therefore, it might be possible to use this vulnerability to extract other fields from the LDAP directory. The exploitability depends on many factors, including whether the application exposes any errors or not, and it also depends on the structure of the LDAP directory.
因此,如果 Subject 包含任何特殊字符,它可以更改查询的语法。在大多数情况下,这可以作为盲 ldap 查询注入加以利用。因此,可以利用此漏洞从 LDAP 目录中提取其他字段。可利用性取决于许多因素,包括应用程序是否公开任何错误,还取决于 LDAP 目录的结构。

In general, whenever you incorporate user-supplied data into an LDAP query, special characters should be properly filtered. That’s exactly how this CVE has been patched in the Bouncy Castle code.
通常,每当将用户提供的数据合并到 LDAP 查询中时,都应正确过滤特殊字符。这正是在充气城堡代码中修补此CVE的方式。

Chapter 3: Certificate revocation and its unintended uses
第 3 章:证书吊销及其意外用途

Similar to Json web tokens, the beauty of certificate chains is that they can be trusted just based on their signature. But what happens if we need to revoke a certificate, so it can no longer be used?
与 Json Web 令牌类似,证书链的美妙之处在于,仅根据其签名即可信任它们。但是,如果我们需要吊销证书,因此无法再使用它,会发生什么?

The PKIX specification (RFC 4387) addresses this problem by proposing a special store for revoked certificates, accessible via HTTP or LDAP protocols. Many developers believe that revocation checking is absolutely necessary, whereas others urge to avoid it for performance reasons or only use offline revocation lists.
PKIX规范(RFC 4387)通过为已吊销的证书提出一个特殊的存储来解决这个问题,该存储可通过HTTP或LDAP协议访问。许多开发人员认为吊销检查是绝对必要的,而其他人则出于性能原因敦促避免它或仅使用脱机吊销列表。

Generally speaking, the store location can be hardcoded into the application or taken from the certificate itself. There are two certificate extensions used for that: Authority Information Access OSCP URL and CRL Distribution points.
一般来说,存储位置可以硬编码到应用程序中或从证书本身获取。有两个证书扩展用于此目的:颁发机构信息访问 OSCP URL 和 CRL 分发点。

mTLS: When certificate authentication is done wrong

Looking at it from the hackers point of view, I think it’s incredible that the location of the revocation server can be taken from the certificate. So, if the application takes URLs relying on AIA or CRLDP extension to make a revocation check, it can be abused for SSRF attacks.
从黑客的角度来看,我认为可以从证书中获取吊销服务器的位置是令人难以置信的。因此,如果应用程序采用依赖 AIA 或 CRLDP 扩展名的 URL 进行吊销检查,则可能会被滥用于 SSRF 攻击。

Sadly for attackers, this normally happens after the signature checks, but in some cases it’s still exploitable.
可悲的是,对于攻击者来说,这通常发生在签名检查之后,但在某些情况下,它仍然可以被利用。

Moreover, LDAP is also supported, at least in Java. You probably heard that, in Java, unmarshaling an LDAP lookup response can lead to a remote code execution. A few years back, Moritz Bechler reported this problem and remote code execution via revocation has since been patched in the JDK. You can check out his blog post for more details.
此外,LDAP也受支持,至少在Java中是这样。您可能听说过,在 Java 中,解送 LDAP 查找响应可能会导致远程代码执行。几年前,Moritz Bechler报告了这个问题,通过吊销的远程代码执行已经在JDK中得到了修补。您可以查看他的博客文章以获取更多详细信息。

In my research, I decided to check if the Bouncy Castle library is also affected. It turns out that Bouncy Castle can be configured to use the CRLDP extension and make calls to an LDAP server. At the same time, Bouncy Castle only fetches a specific attribute from the LDAP response and does not support references. So, remote code execution is not possible there. HTTP SSRF is still viable though.
在我的研究中,我决定检查充气城堡图书馆是否也受到影响。事实证明,Bouncy Castle可以配置为使用CRLDP扩展并调用LDAP服务器。同时,Bouncy Castle 仅从 LDAP 响应中获取特定属性,不支持引用。因此,那里无法远程执行代码。HTTP SSRF仍然是可行的。

private static Collection getCrlsFromLDAP(CertificateFactory certFact, URI distributionPoint) throws IOException, CRLException
{
    Map<String, String> env = new Hashtable<String, String>();

    env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(Context.PROVIDER_URL, distributionPoint.toString());

    byte[] val = null;
    try
    {
        DirContext ctx = new InitialDirContext((Hashtable)env);
        Attributes avals = ctx.getAttributes("");
        Attribute aval = avals.get("certificateRevocationList;binary");
        val = (byte[])aval.get();
    }

Example: CVE-2023-28857 credentials leak in Apereo CAS
示例:Apereo CAS 中的 CVE-2023-28857 凭据泄漏

I also had a quick look at open source projects that support mTLS and perform revocation checking. One of these projects was Apereo CAS. It’s another popular authentication server that is highly configurable. Administrators of Apereo CAS can enable the revocation check using an external LDAP server by specifying its address and password in the settings:
我还快速浏览了支持 mTLS 并执行吊销检查的开源项目。其中一个项目是Apereo CAS。它是另一种高度可配置的流行身份验证服务器。Apereo CAS 的管理员可以通过在设置中指定其地址和密码,使用外部 LDAP 服务器启用吊销检查:

cas.authn.x509.crl-fetcher=ldap
cas.authn.x509.ldap.ldap-url=ldap://example.com:1389/
cas.authn.x509.ldap.bind-dn=admin
cas.authn.x509.ldap.bind-credential=s3cr3taaaaa

If these settings are applied, Apereo CAS performs the revocation check for the certificate, fetching the address from the certificate’s CRLDP extension.
如果应用了这些设置,Apereo CAS 将对证书执行吊销检查,从证书的 CRLDP 扩展中获取地址。

/**
* Validate the X509Certificate received.
*
* @param cert the cert
* @throws GeneralSecurityException the general security exception
*/
private void validate(final X509Certificate cert) throws GeneralSecurityException {
   cert.checkValidity();
   this.revocationChecker.check(cert);

   val pathLength = cert.getBasicConstraints();
   if (pathLength < 0) {
       if (!isCertificateAllowed(cert)) {
           val msg = "Certificate subject does not match pattern " + this.regExSubjectDnPattern.pattern();
           LOGGER.error(msg);

I was afraid that this could lead to remote code execution, but it turns out that Apereo CAS uses a custom library for LDAP connection, which does not support external codebases or object factories needed for RCE.
我担心这可能会导致远程代码执行,但事实证明,Apereo CAS 使用自定义库进行 LDAP 连接,不支持 RCE 所需的外部代码库或对象工厂。

When I tested this in Apereo CAS, I noticed one interesting behavior. The server prefers the LDAP URL located inside the certificate, instead of the one that is configured in settings. At the same time, Apereo CAS still sends the password from the settings. I quickly set up a testing environment and sent a self-signed certificate in the header. My self-signed certificate had a CRLDP extension with the LDAP URL pointing to a netcat listener. After sending this request to Apereo CAS, I received a request to my netcat listener with the username and password leaked.
当我在Apereo CAS中对此进行测试时,我注意到了一个有趣的行为。服务器首选位于证书内的 LDAP URL,而不是在设置中配置的 URL。同时,Apereo CAS仍然从设置中发送密码。我快速设置了一个测试环境,并在标头中发送了一个自签名证书。我的自签名证书有一个 CRLDP 扩展名,其中的 LDAP URL 指向网络猫侦听器。将此请求发送给 Apereo CAS 后,我收到了一个发送给我的 netcat 侦听器的请求,其中用户名和密码泄露了。

mTLS: When certificate authentication is done wrong

After reporting this vulnerability, the application developers issued a fix within just one day. They patched it by clearing the login and password used for LDAP connection if the URL is taken from the CRLDP. Therefore, the password leak is no longer possible. Nevertheless, I would say that using URLs from the CRLDP extension is still dangerous, as it broadens the attack surface.
报告此漏洞后,应用程序开发人员在短短一天内发布了修复程序。如果 URL 取自 CRLDP,他们通过清除用于 LDAP 连接的登录名和密码来修补它。因此,密码泄漏不再可能。尽管如此,我想说的是,使用 CRLDP 扩展中的 URL 仍然是危险的,因为它扩大了攻击面。

Summary 总结

If you’re developing an mTLS system or performing a security assessment, I suggest:
如果您正在开发mTLS系统或执行安全评估,我建议:

  1. Pay attention when extracting usernames from the mTLS chain, as the servers only verify the first certificate in the chain.
    从 mTLS 链中提取用户名时要注意,因为服务器只验证链中的第一个证书。
  2. Use Certificate Stores with caution, as it can lead to LDAP and SQL injections.
    请谨慎使用证书存储,因为它可能会导致 LDAP 和 SQL 注入。
  3. Certificate revocation can lead to SSRF or even to RCE in the worst case. So, do the revocation check only after all other checks and do not rely on URLs taken from the certificate extensions.
    证书吊销可能导致 SSRF 在最坏的情况下甚至导致 RCE。因此,仅在所有其他检查之后执行吊销检查,并且不要依赖从证书扩展中获取的 URL。

原文始发于Github:mTLS: When certificate authentication is done wrong

版权声明:admin 发表于 2023年9月1日 上午9:24。
转载请注明:mTLS: When certificate authentication is done wrong | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...