A New Way of Jolokia Remote Code Execution

0x01 Preface

Last week, I saw an interesting case about JDBC attack reported by @JJaaskela in HackerOne. It caught my attention, so I intended to analyse this case.

upload successful

Here is the link of this report:

https://hackerone.com/reports/1547877

Due to the bug fix, I cannot reproduce the vulnerablity. But I still can obtain some clues according to the description of this report.

Aiven is a next-generation managed cloud database service that hosts your software infrastructure services. It supports diverse kinds of data sources, like Apache Kafka, PostgresSQL, MySQL, Redis, etc. Obviously, JDBC connections exist in these scenarios.

The root causes of this vulnerablity are as follow:

  • The reporter found the service supported Jolokia service according to the logs in web console, and the HTTP sink connector allowed user to send HTTP request to localhost, Jolokia service was listening on localhost:6725 as well.

  • JMX exposes the Mbean called com.sun.management:type=DiagnosticCommand, it has a special operation named jvmtiAgentLoad.

  • The HTTP Sink Connector did not verify the data source target whether or not a local resource, so we can utilized the operation jvmtiAgentLoad to load a local jar file. In this case the jar file is the SQLite database file.

Loading class

As we know, we can use different ClassLoaders to load java classes from diverse origins. The main origins as follow:

  1. Loading .class file from local system directly, which is the loading method of most classes.

  2. Loading .class file from archives such as zip, jar, etc.

  3. Loading .class file or data through the network.

  4. Extract .class file from proprietary databases.

  5. Upload the Java source file to the server, dynamically compile it into .class file and perform loading.

So we can create a malicious jar file and then insert the jar file data into the SQLite database as the BLOB data type.

0x02 About Jolokia

First of all , let’s review the Jolokia concept and its historical vulnerablities.

Jolokia is a JMX-HTTP bridge giving an alternative to JSR-160 connectors. It is an agent based approach with support for many platforms. In addition to basic JMX operations it enhances JMX remoting with unique features like bulk requests and fine grained security policies.

In the past days, there are several security researchers have shared their findings about Jolokia.They disclose a lot of weaknesses that exist in the Jolokia component. If you are interested in the vulnerabilities, you could review their write-ups.

https://www.veracode.com/blog/research/exploiting-spring-boot-actuators

https://thinkloveshare.com/hacking/ssrf_to_rce_with_jolokia_and_mbeans/

I’m sure you’re aware Jololia often exposes many Mbeans, many of which are utilized to triger remote code execution vulnerabilities. In this case, we talk about the Mbean named com.sun.management:type=DiagnosticCommand

0x03 JVMTI & Instrument

In this case, Jolokia exposes the com.sun.management:type=DiagnosticCommand MBean, it has a risky operation named jvmtiAgentLoad. So what is JVMTI?

JVMTI(JVM Tool Interface) which is the native interface provided by the java virtual machine. JVMTI is just a set of interfaces. If we want to develope JVM tools, we need to write an agent programme to use these interfaces. Agent programme is atually a C or C++ language written dynamic link library. So loading a malicious .so file can lead remote code exection. However, Java introduced Instrumentation since in JDK 5.

Using Instrumentation interface, we can call the dynamic library of libinstrument through Java code to interact with the JVMTI interface, eliminating the need to develop native dynamic link library files.

The Instrument mechanism includes two integration forms: one is the main method is executed before startup, and the other is the main method is loaded internally through attach.

  • premain (Agent mode): Main method is invoked before the target application starting.
1
java -javaagent:/path/to/javaagent.jar -jar application.jar

The argument -javaagent needs to be in front of -jar. Otherwise, it will not take effect.

1
2
public static void premain(String agentArgs, Instrumentation inst);
public static void premain(String agentArgs);

The premain method is relatively simple. It’s a jar file of the java agent. After adding this jar to the startup command, the premain method will be run before the main method is started. It should be noted that to make the jar file know which premain method to start, we also need to define it in the manifest file. There are also two ways to define a menifast. One is to write a menifast file directly, and the other is to use Maven’s plug-in to write it.

  • agentmain(Attach mode): In addition to the target application, use an attach application to inject javaagent.jar into the target application.

    1
    2
    public static void agentmain(String agentArgs, Instrumentation inst);
    public static void agentmain(String agentArgs);

The attach method is relatively troublesome. You need to set up a separate application (or use a different thread), find all running VirtualMachineDescriptors through VirturalMachine. list(), match them to the target application, and then inject javaagent.jar into the target application.

If you have known the above knowledge, it is not difficult to realize using the jvmtiAgentLoadoperation of com.sun.management:type=DiagnosticCommand mbean,we can inject malicious java agent into the application by attach mode without restarting target application.

0x04 Create malicious Jar file

Firstly, the JDK provides two static methods, premain and agentmain, which can be used directly. Here I use the agentmain method.

upload successful

If you create MANIFEST.MF manually, you need to specify the Agent-Class, and finally build the jar file.

upload successful

It should be noted here that if a standard so file supported by JVMTI is called, there will be an error.

"Agent_OnAttach is not available in /tmp/ext.so "

upload successful

The reason is that the JVMTI is invoked through the Agent_ OnAttach as the entry function, and then execute the following process to load the Java agent.

  1. Get JNIEnv to ensure that it has been successfully attached to the Java process.

  2. Create and initialize JPLISAgent, then make VMInit monitoring (it will not be triggered), and the logic is the same as OnLoad.

  3. Read Agent-Class and load it.

  1. Read the META-INFO related configuration and set the mRetransformEnvironment ClassFileLoadHook listening. The logic is the same as OnLoad.

  2. Create an InstrumentationImpl instance.

  3. Set the mNormaltransformEnvironment ClassFileLoadHook listening

  4. Execute AgentMain method

The Java agent code as follow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class JavaAgent {
private static final String RCE_COMMAND = "open -a calculator";

public static void cmd() {
try {
Runtime.getRuntime().exec(RCE_COMMAND);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void agentmain(String args, Instrumentation inst) {
System.out.println("In JavaAgent Agentmain");
cmd();
}
}

Inject malicious jar file stream into database file

Just use the JDBC Sink Connector feature.When establishing the SQLite JDBC connecting , a SQLite database file is created automatically, then insert the malicious jar file data into the database table.

According the Oracle official document definition:

upload successful

We can clearly know that the jar file is a kind of archives, like zip, tar,etc. It has no restriction about the file name.Consequently we can embed the jar files in other files just like zip files, without affecting their normal usage.

Eventually,when the SQLite database file exists in the local disk, using jvmtiAgentLoad operation to load the specified jar file.

Final Inllustration

Owing to the vulnerability being fixed, I set up a local environment to reproduce it.

  • Inject malicious Java agent into SQLite database file

  • During JDBC connection, if the database file does not exist ever, a SQLite database file foo.jar will be automatically created through the getConnection method of DriverManager and then create a table through the other corresponding SQL statement.

  • Write the malicious Java agent into the database as Blob data type. In my illustration, I write the malicious agent.jar into the SQLite database file test.jar.

upload successful

  • Load Java agent
1
http://127.0.0.1:8099/actuator/jolokia/exec/com.sun.management:type=DiagnosticCommand/jvmtiAgentLoad/!/tmp!/agent.jar
  • Successfully attach the malicious Java agent and complete the RCE

upload successful