 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 a Mutual TLS? In short, it's another form of client authentication. Most of you probably know that the browser checks the server's 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 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 plain text. 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. Subject usually defines a username or host name, and similar to JSON web tokens, the certificate is signed by the issuer's private key, so the data inside cannot be tampered with. The issuer field points to another certificate who signs the certificate. A sequence of certificates signed in 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 client, 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 mappings, 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 used for client authentication. Even if this certificate is issued for another purpose, it still can potentially be used for TLA authentication. Furthermore, the mutual TLS does not specify how this certificate should be issued. It's up to the application to define additional mechanisms 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 you can somehow forge the content of the certificate but keep the same signature, we can completely break the authentication process. Also, since X549 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 the 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 TLS handshake. For example, they may need it for authorization needs to understand what is the current username. In Java, you can use SSLsession.GetPerCertificates 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 MTLSRFC explicitly says that 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 properly changed to a root CA. The last certificate might be self-signed 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 MTLite authentication. For exploitation, we need one that doesn't require any additional factors, such as tokens or secrets. Client management register node is a good example. As it mutates user's data. In the first comment, it's shown 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 the 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 connection is terminated 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 engine configuration to enable MTLS. I've seen a number of systems like that. In most cases, backend servers behind engines do not perform additional validation trust in 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 a request 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 revealed. Over the past few years, we've seen a lot of requests and headers smuggling vulnerabilities, including the latest CVs in Apache and Node.js. Be careful when implementing these scenarios and check the certificate signature on all servers if possible. 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 the certificates during chain validation. These stores are implemented over different protocols, such as HTTP, LDAP, FTP, or SQL queries. The x5.9 format defines some certificate extensions that can contain information where to find the issuer NCA 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 issuer, serial, and their alternative names can be used to construct SQL and LDAP queries, leaving options for injection attack. When certificates 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 LDAP's third 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 escaping. 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 TV has been patched in the Bouncy Castle code. Next, we'll cover evocation. This controversial topic sparked many debates over the years. 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? 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 a flamed 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 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 probably heard that in Java, in marshalling 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 CRLTP extension and make calls to an LDAP server. At the same time, Bouncy Castle only features a specific attribute from the LDAP response and does not support references. So, remote code execution is sadly not possible there. Each TTPS SSRF 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 a revocation check for the certificate, fetching the address from the certificate's CRLTP extension. I was afraid that this could also lead to a 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 a password from the settings. I quickly set up a testing environment and send a self-signed certificate in the header. This certificate has CRLTP extension with LDAP URL pointing to a NetCutListener. After sending this request to Apirio CAS, I managed to receive a request to my NetCutListener 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 using URLs from CRLTP extension is still dangerous, as it broadens the attack surface. To summarize, when you develop an MTLS system or performance security assessment, I suggest, first, pay attention when extracting user names from the MTLS chain, as the servers only verify the first certificate in the chain. Second, use certificate stores with Caution. It can lead to LDAP 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 and github. Bye!