shift or die

security. photography. foobar.

Turning off certificate validation with Java instrumentation

A colleague and I recently lamented the lack of Frida-like tools for Java. When analyzing Java-based fat-client application, our workflow would most of the time consist of decompiling, analyzing and then re-compiling »interesting« classes which we modified to change behaviour or output some internal state (such as a network packet before it gets encapsulated in an encryption layer). Luckily, said colleague discovered the »Guide to Java Instrumentation« article by Adrian Precub though, which shows how to add dynamic instrumentation either at startup or runtime. This is done by making use of the Java instrumentation API and Javassist, which allow us to add Java code to existing methods.

I implemented a quick tool based on the blog post which allows you to build a so-called Java Agent that can be loaded on application start-up and modifies methods of your choice. In this blog post, I will walk you through an example, showing how to turn off certificate validation in an example application.

Let’s look at our example application which tries to retrieve content from https://self-signed.badssl.com:

import java.io.BufferedReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.InputStreamReader;

public class Example {
    public static void main(String[] args) throws Exception {
        Thread.sleep(5000);
        URL url = new URL("https://self-signed.badssl.com/");
        HttpURLConnection con = (HttpURLConnection) url.openConnection();

        BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
        String inputLine;
        while ((inputLine = in.readLine()) != null) {
            System.out.println(inputLine);
        }
    }

When we compile and run it, it fails due to the fact that Java (rightfully) does not trust the self-signed certificate:

$ javac Example.java
$ java Example
Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
[...]

Digging a bit through the JRE source code, we can see that the checkServerTrusted method in sun.security.ssl.X509TrustManagerImpl will be called and if it does not throw an exception, the server certificate is considered trusted. Let’s modify this method by using the Java Instrumentation Tool:

$ wget https://github.com/alech/java-instrumentation-tool/releases/download/0.1/java-instrumentation-tool-0.1.jar
$ echo 'sun.security.ssl.X509TrustManagerImpl,checkServerTrusted,java.security.cert.X509Certificate[];java.lang.String;java.net.Socket,insertBefore,trustmanager.java' > hooks.txt
$ mkdir hooks
$ echo 'return;' > hooks/trustmanager.java
$ java -jar java-instrumentation-tool-0.1.jar hooks.txt

We can see that we need to give the tool a class, method, type signature, place where to modify the code (at the beginning of the method – insertBefore or before every return statement – insertAfter) and a filename which contains the source code we want to add. In this case, we simply add a return statement at the top of the method in order to leave it quickly before it has a chance of throwing an exception.

The tool created an agent.jar file for us which we can load when running our example application by specifying the -javaagent parameter:

java -javaagent:agent.jar Example
[Agent] transforming sun.security.ssl.X509TrustManagerImpl.checkServerTrusted
[Agent] ClassTransformer constructor, sun.security.ssl.X509TrustManagerImpl, null
[Agent] Transforming class sun.security.ssl.X509TrustManagerImpl, method checkServerTrusted, param types java.security.cert.X509Certificate[];java.lang.String;java.net.Socket
[Agent] adding code before checkServerTrusted
<!DOCTYPE html>
<html>
<head>
[...]

We can see some output from the agent, and then we can see that this time, the TLS handshake succeeded and we get the output from the webserver as if it were using a trusted certificate.

Obviously, this is not all you can do with Java instrumentation, your imagination on what you want to change or find out is the limit ☺️.