Welcome, everyone. My name is Michael Stepankin. In this session, we'll dive into Mutual TLS and authentication based on certificates. I'm excited to present some interesting attacks on MTLS, as well as demonstrate vulnerabilities in popular open source software. My purpose is to give you some fresh ideas on how to attack and secure this technology. So, what is Mutual TLS? In short, it's another form of client authentication. Most of you probably know that the browser checks a server certificate. The same idea can be applied to authenticate browsers or TLS clients in general. For example, a server can ask a browser to provide a valid certificate. It can be either used as an alternative to classic password authentication, or as an additional factor. This process is defined in the TLS RFC and based on asymmetric cryptography. During the handshake, the server can send a certificate request message. The client replies with a certificate message that contains a public key. This is followed by a certificate verify message encrypted by a private key, so that even if the message is intercepted, a person in the middle would not see any secrets in the place. So, what exactly is a certificate? Generally speaking, it's a blob of binary encoded data. The most important fields are the subject, the issuer, and the public key. The subject usually defines a username or hostname. Similar to JSON Web Tokens, the certificate is signed by the issuer's private key, so the data inside cannot be tampered with. The issue field points to a another certificate who signed this certificate. A sequence of certificates signing each other forms a certificate chain. In order to check the validity of the certificate, the server should build a trusted chain from the client certificate to a trusted root certificate. The client and intermediate certificates are normally sent by the clients whereas the server only needs to know about trusted root certificates. Let's see how to use MTLS in an example Java web application. In the bare minimum, the developers only need to enable it in the application settings and specify the location of all trusted root certificates. From the client perspective, such as cURL, you need to specify what certificate is sent to the server. The rest of the application code, such as request mapping, is exactly the same as for normal web application. As you can see, this approach brings some advantages and disadvantages compared to traditional authentication methods, such as password. First of all, it might be faster for your application as the authentication can only happen at the handshake level, so all subsequent HTTP requests will be authenticated, saving our CPU time. Also, similar to JSON Web Tokens, there is a storage benefit, as the server does not need to store all the client certificates, only need to access the root certificate. Speaking of the cons, there is no granular control. As soon as you enable MTLS in your application, all requests have to be authenticated, even to the public pages or JS or CSS resources. Also, any certificates signed by a root certificate can be authenticated. For example, the MUTL-TLS can be used for client authentication. Even if the certificate is issued for another purpose, it still can potentially be used for TLA authentication. Furthermore, the MUTL-TLS does not specify how this certificate should be issued. It's up to the application to define additional mechanism for certificate issuance. Finally, certificates are always expired, and it's just a hassle to rotate them frequently. Before we dive into the attack section, I'm going to briefly mention some of the previous well-known attacks on certificate 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. Also, since X.509 format is quite complex, just parsing these data structures can lead to buffer and heap overflows. In addition, lack of basic constraint checking can also be used to bypass the authentication process. I've got to say that in my research, I mostly looked at how MTLS is implemented in applications written in Java. In Java Development Kit, most of these attacks are already mitigated on the language level, so I had to take a different approach. At the same time, the attacks I'm going to describe are also applicable to other languages. All right, let's move on to the attack section. In the first chapter, we'll cover improper certificate extraction. In real-life applications, developers often need to access the subject name from the certificate presented during the TLX handshake. For example, they may need it for authorization needs to understand what is the current username. In Java, you can use the SSLSession.getPairCertificates method. Interestingly, this method returns an array of certificates, not a single one. Why? Perhaps because TLS specification defines that clients may send a full chain of certificates, not only one. 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 a client certificate. This is correct. As MTLS RFC explicitly says, the sender certificate must come first in the list. 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. This can be dangerous, as the underlying TLS library in Java only verifies the first certificate from the list. One of these examples was of vulnerability I discovered in KeyClock. KeyClock is a popular authorization server that supports auth, SAML, and other authorization methods, as well as Mutual TLS. KeyClock iterates over all certificates in the array, searching for the one that matches the client ID form parameter. As soon as it found one certificate, it implicitly trusts it, assuming that its signature has already been checked during the TLS handshake. In reality, a client can send as many certificates as they want, so 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. The last certificate will be issued to the client and belongs to a different user. The client does not even need to provide a valid private key for it. There are a number of endpoints in KeyClock that support MTL authentication. For exploitation, 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 users' data. In the first comment, it shows how we can normally use this API with MTLS. In the second request, we generate a new self-signed certificate using OpenSSL and add it to the chain. When we send the second curl request, KeyClock will perform this action on behalf of the user specified in certificate 2 instead of certificate 1. Therefore, we can mutate data on the server for any client, that supports MTLS. How to fix that? Easy. Just use the first certificate from the array. That's exactly how KeyClock patched this vulnerability. It's a good example of how developers provide methods and interfaces that can be misunderstood or used incorrectly. Another common scenario for MTLS deployments is when TLS connections are not available on the reverse proxy. In this case, the reverse proxy often checks the certificate and forwards it to a backend server as an additional header. On this slide, you can see a typical NGIN configuration to enable MTLS. I've seen a number of systems like that, and in most cases, backend servers behind NGINs do not perform additional validation, trusting the reverse proxy. This behavior is not exploitable straight away, but it's not ideal either. Why? Well, first of all, it means that any server in the local network can make requests with the header, so the network segment should be properly isolated from the traffic coming from outside. Additionally, if your backend or reverse proxy is affected by a request smuggling vulnerability, its exploitation becomes trivial. Over the past few years, we've seen a lot of requests and headers smuggling vulnerabilities, including the latest CVEs in Apache and Node.js. Be careful when implementing these scenarios and check the certificate signature on all servers. Moving on to the next chapter, follow the chain where it leads you. In large systems, servers may not store all root and intermediate certificates locally, but use an external storage instead. RFC defines 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. The X.509 format defines some certificate extensions that can contain information where to find the issuer and CA certificates. For instance, the Authority Information Access 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, issue, serial, and their alternative names can be used to construct SQL and LDAP queries. Leaving options for injections attack. When certificate stores are in use, you want to think about these values as insertion points, similar to those ones we have in Burp Suite Intruder. And what's really good for attackers, all of these values can be used in queries before the signature is checked. To demonstrate an example of this vulnerability, we'll use an LDAP search store 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. Under the hood, Bouncy Castle uses a subject field from the certificate to build an LDAP query. The subject field is inserted in the filter and, as you can imagine, without any changes. So, if the subject contains any special characters, it can change the syntax of the LDAP query. In most cases, this can be exploited as a blind LDAP query injection. Therefore, it could be possible to use this vulnerability to extract other fields from the LDAP directory. The exploitation is based on many factors, including whether the application exposes any errors, and it also depends on the structure of the LDAP directory. Anyway, whenever you incorporate user-supplied data into a LDAP query, special characters should be properly filtered. That's exactly how this CVE has been patched in the Bouncy Castle code. Next, we'll cover revocation. This controversial topic sparked many debates over the years. Similar to JSON Web Tokens, the beauty of certificate chains is that they are not limited to a single domain. 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? The PKI specification addresses this problem by proposing a special store for revoked certificates, accessible via HTTP or LDAP protocols. Certificate verifiers can use two different methods, OACP or CRL. Many developers believe that revocation checking is absolutely necessary, whereas others urge to avoid it for performance reasons or use offline revocation lists. Generally speaking, the store location can be hard-coded into the application or taken from the certificate itself. There are two certificate extensions used for that, authority information access and CRL distribution points. Looking at it from the hacker's 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 extensions to make a revocation check, it can be abused for SSRF attacks. Sadly for attackers, this normally happens after the signature checks, but in some cases it's still exploitable. Moreover, the LDAP protocol is also supported, at least in Java. Wait, did I hear LDAP in Java? Yes. The standard Java library can use JNDI to access revocation locations under some settings. You've probably heard that in Java, in-marshaling an LDAP lockup response can lead to a remote code execution. A few years back, this problem was reported by Moritz Bechler, and remote code execution via revocation has been patched in GDK. You can check out his blog post for more details. In my research, I decided to check if bouncy castle library is also affected. It turned out that the bouncy castle can 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 sadly not possible there. HTTPSSRF is still a viable thing, though. I also had a quick look at open-source projects that support MTLS and perform revocation checking. One of these projects was Apirio CAS. It's another popular authentication server that is highly configurable. Administrators of Apirio CAS can enable the revocation check using the external LDAP server by specifying its address and password in the settings. If these settings are applied, Apirio CAS performs the revocation check for the certificate, fetching the address from the certificate's CRLDP extension. I was afraid that this could also lead to remote code execution, but it turned out that Apirio CAS uses a custom library for LDAP connection, which does not support external code bases or object factories needed for RCE. When I tested this in Apirio CAS, I noticed one interesting behavior. The server prefers LDAP URLs located inside the certificate, instead the one that is configured in the settings. At the same time, Apirio CAS still sends the password from the settings. I quickly set up a testing environment and send a self-signed certificate in the header. This certificate has CRLDP extension with LDAP URL pointing to a netcat list. After sending this request to Apirio CAS, I managed to receive a request to my netcat listener with a username and password leaked. After reporting this vulnerability, the application developers issued a fix within just one day. They patched it by clearing the login and password from LDAP connection. Therefore, the password leak is no longer possible. But in general, I'd say that you should be careful with this. To summarize, when you develop an MTLS system or perform a security assessment, I suggest First, pay attention when extracting user names from the MTLS chain, as the server only verifies the first certificate in the chain. Second, use certificate stores with caution. It can lead to else. up and SQL injections. And last but not least, certificate revocation can lead to SSRF or even RCE in the worst case. So it's advised to do the revocation check only after all other checks. And that's it. Thank you for your attention. The full write-up will be available in our blog at GitHub. Bye.