~8 minutes
Un endpoint de l’interface de gestion RabbitMQ répond à n’importe qui, sans authentification : GET /api/auth. Et il retourne le secret client OAuth configuré sur le broker. À partir de là, un attaquant obtient un jeton administrateur auprès du fournisseur d’identité, se présente à RabbitMQ avec les pleins pouvoirs, et contrôle messages, files, utilisateurs et configuration. Le bug était dans le code depuis début 2024.
Ce que Miggo a trouvé
Deux vulnérabilités, publiées sous une disclosure coordonnée avec les mainteneurs de RabbitMQ, référencées GHSA-pj24-8j6m-vq9q et GHSA-9q2j-2hq8-22r2.
| CVE | Sévérité | Auth requise | Composant |
|---|---|---|---|
| CVE-2026-57219 | Critique (CVSS 8.7) | Non | GET /api/auth, plugin de gestion |
| CVE-2026-57221 | Modérée (CVSS 5.3, CWE-862) | Oui (compte sans permission) | queue.declare / exchange.declare passifs |
Les deux touchent les versions 3.13.0 et suivantes, patchées en 4.3.0, 4.2.6, 4.1.11, 4.0.20 et 3.13.15.
Ce qui rend intéressante cette découverte n’est pas tant la gravité individuelle des bugs, plutôt banale une fois expliquée, que la méthode qui les a fait remonter : VulnHunter n’a pas cherché un pattern connu, il a repéré une incohérence structurelle, un endpoint qui s’autorise différemment de ses voisins fonctionnels. C’est le genre de faille qu’un audit manuel pressé par le temps regarde en diagonale, précisément parce qu’elle ne ressemble à rien de connu.
Le mécanisme : CVE-2026-57219
Le code du endpoint est instructif. Dans rabbit_mgmt_wm_auth.erl, le callback is_authorized/2 est codé en dur pour retourner {true, ...} inconditionnellement. Aucune vérification de session, de jeton, ou de rôle. produce_auth_settings/2 construit ensuite une réponse JSON qui inclut, sans filtrage, le champ oauth_client_secret.
sequenceDiagram
participant A as Attaquant
participant RMQ as RabbitMQ :15672
participant IdP as Fournisseur d'identité (Auth0/Azure AD/Keycloak/UAA)
A->>RMQ: GET /api/auth (sans jeton)
RMQ-->>A: 200 OK { oauth_client_secret: "..." }
A->>IdP: Demande de jeton avec client_secret volé
IdP-->>A: Jeton d'accès administrateur
A->>RMQ: Connexion AMQP avec le jeton
RMQ-->>A: Contrôle total : files, messages, utilisateurs, config
Cinq requêtes, aucune credential de départ. C’est ce que Miggo qualifie de “full broker takeover”, et l’expression n’est pas exagérée : une fois le jeton administrateur obtenu, l’attaquant a les mêmes droits qu’un opérateur légitime sur l’ensemble du broker.
Un endpoint qui retourne
{true, ...}sans condition n’est pas un bug d’autorisation. C’est l’absence d’autorisation, déguisée en fonction qui en porte le nom.
La population exposée n’est pas anecdotique : tous les déploiements RabbitMQ utilisant OAuth 2 avec management.oauth_client_secret configuré, ce qui couvre une bonne partie des intégrations Auth0, Azure AD/Entra ID, Keycloak ou UAA. Le risque grimpe d’un cran dès que le port 15672 est joignable depuis un réseau qui n’est pas strictement de confiance, ce qui, sur pas mal d’architectures Kubernetes que j’ai pu observer, arrive plus souvent qu’on ne le pense pour un port “juste pour l’admin”.
Note du PandaRedacteur : “juste pour l’admin” est à peu près la phrase la plus dangereuse du vocabulaire réseau. Elle précède systématiquement une règle de firewall trop permissive et un ticket d’incident six mois plus tard.
Le mécanisme : CVE-2026-57221
Deuxième bug, moins spectaculaire mais révélateur de la même classe d’erreur. Le chemin “passive declare” de queue.declare et exchange.declare (mode où le client demande juste de vérifier qu’une ressource existe, sans la créer) ignore complètement le contexte d’autorisation. Les paramètres concernés sont préfixés d’un underscore, convention Erlang signalant “volontairement inutilisé” ; sauf qu’ici, ce qui est volontairement ignoré, c’est le contrôle de permission lui-même. Toutes les opérations comparables, queue.delete, basic.consume, basic.publish, effectuent correctement cette vérification.
Un compte AMQP valide mais sans aucune permission suffit. Il ouvre une connexion, envoie des passive queue.declare en boucle sur des noms de files devinés ou énumérés, et le serveur confirme leur existence avec les comptages de messages associés. Répété sur un vhost partagé entre plusieurs tenants, cela donne une cartographie complète de l’activité métier d’autres clients, sans qu’aucune ligne de log ne distingue ce trafic d’un usage légitime.
On peut distinguer ici deux logiques d’attaque très différentes sous une même famille de bug : la première (CVE-2026-57219) est une porte grande ouverte, la seconde (CVE-2026-57221) est une fissure d’observation. Mais dans un environnement multi-tenant où le modèle de permissions RabbitMQ constitue l’unique frontière entre clients, cette fissure suffit à casser l’hypothèse d’isolation sur laquelle repose toute l’architecture, un sujet que j’ai déjà creusé sous un autre angle dans AST06: Isolation faible.
Analyse STRIDE
| Catégorie STRIDE | Applicable | Explication |
|---|---|---|
| Spoofing (Usurpation d'identité) | Oui | Le secret OAuth volé permet à l'attaquant d'obtenir un jeton légitime auprès de l'IdP et de se faire passer pour un administrateur du broker. |
| Tampering (Falsification) | Oui | Un administrateur usurpé peut modifier files, exchanges, bindings, politiques et utilisateurs. |
| Repudiation (Répudiation) | Modéré | L'appel `GET /api/auth` non authentifié n'est pas nécessairement rattachable à un identifiant, ce qui complique l'attribution forensique. |
| Information Disclosure (Divulgation) | Oui | Le secret OAuth lui-même (CVE-2026-57219) et les métadonnées de queues/exchanges d'autres tenants (CVE-2026-57221) sont directement exposés. |
| Denial of Service (Déni de service) | Faible | Aucune des deux CVE ne documente d'impact direct sur la disponibilité ; un attaquant avec accès admin pourrait néanmoins en provoquer un, mais ce n'est pas le vecteur décrit. |
| Elevation of Privilege (Élévation de privilèges) | Oui | C'est le cœur de CVE-2026-57219 : un attaquant anonyme finit avec des privilèges administrateur complets sur le broker. |
Impact potentiel
| Impact | Niveau | Description |
|---|---|---|
| Confidentialité | Critique | Secret OAuth exposé sans authentification ; contenu des messages et métadonnées inter-tenants accessibles après exploitation. |
| Intégrité | Critique | Un attaquant avec jeton admin peut modifier ou supprimer files, exchanges, utilisateurs, politiques. |
| Disponibilité | Élevé | Non documenté directement dans l'advisory, mais un accès admin complet permet trivialement la purge ou la suppression de files critiques. |
| Réputation | Sévère | Un broker de messages compromis touche potentiellement tous les services en aval qui en dépendent ; l'incident se propage bien au-delà du composant initial. |
Mitigations
- Patcher immédiatement. Montez vers 4.3.0, 4.2.6, 4.1.11, 4.0.20 ou 3.13.15 selon votre branche. Pas de contournement de configuration qui neutralise complètement CVE-2026-57219 sans le correctif.
- Faites une rotation du secret OAuth. Si le port 15672 a jamais été joignable depuis un réseau non fiable, supposez le secret compromis et régénérez-le côté IdP, sans attendre une preuve d’exploitation.
- Isolez le port 15672. L’interface de gestion n’a rien à faire exposée au-delà d’un réseau d’administration strictement filtré, VPN ou bastion compris.
- Séparez vos tenants par vhost distinct, pas par permissions dans un vhost partagé. C’est la seule mitigation réelle contre CVE-2026-57221 : le modèle de permissions RabbitMQ n’était pas conçu comme frontière de sécurité forte entre locataires non fiables.
- Vérifiez vos images figées. Conteneurs pinnés, charts Helm, produits tiers embarquant RabbitMQ : la vulnérabilité traîne depuis début 2024, donc une image “stable” datant d’avant le patch reste vulnérable même si le dépôt source, lui, est à jour. C’est exactement le type de dérive que je décrivais dans AST02: Supply Chain Compromise et dans AST07: Update Drift.
# Vérification rapide de la version exposée par l'interface de gestion
import requests
def check_rabbitmq_version(base_url: str) -> str:
resp = requests.get(f"{base_url}/api/overview", timeout=5)
resp.raise_for_status()
return resp.json().get("rabbitmq_version", "inconnue")
# Comparer contre les versions patchées : 4.3.0, 4.2.6, 4.1.11, 4.0.20, 3.13.15
Pour prioriser ce correctif face au reste de votre backlog de vulnérabilités, la matrice CVE/CVSS/EPSS/KEV reste mon outil de référence : un CVSS de 8.7 sur un composant exposé publiquement, sans authentification requise, se traite en urgence, pas au prochain sprint de maintenance.
Note du PandaRedacteur : je note aussi que si vous n’avez toujours pas de vhost séparé par tenant sur votre RabbitMQ partagé, cet article est un bon prétexte pour l’ajouter à votre backlog avant que quelqu’un d’autre ne vous le rappelle via un incident.