Une illustration abstraite générée sur un fond bleu marine profond. Une large structure de pont couleur ardoise traverse le cadre, et un port rectangulaire sur sa face gauche est ouvert et luit d'une couleur ambre. À travers cette ouverture, un flux de petits blocs identiques se déverse vers l'extérieur sur une légère grille de plan bleu, traversant une fine limite cyan en pointillés dessinée au premier plan sans être interrompu. En bas à droite, les blocs alimentent un treillis translucide bas de nœuds connectés qui ressemble à un stockage de mémoire. Aucun texte, lettre, chiffre, personne, marque commerciale ou logo d'organisation n'apparaît. Le logo OrcaRouter est incrusté dans le coin inférieur droit d'une bande blanche située sous l'illustration.
AI Safety Incidents

RufRoot : une seule requête non authentifiée vers la passerelle MCP de Ruflo a exécuté des commandes, volé des clés et empoisonné la mémoire de l'IA

Auteur

Rowan Sterling

Date de publication

Derniers modèles · 20Voir tous les modèles →
Benchmarks : Artificial Analysis · mis à jour quotidiennement
Retour à tous les articles

Le 30 juin 2026, Noma Labs a remis au mainteneur de Ruflo un exploit fonctionnel contre le projet lui-même. Le pôle de recherche de Noma Security avait découvert que Ruflo — une plateforme open source d'orchestration d'agents, anciennement publiée sous le nom de Claude Flow — livrait un déploiement Docker Compose par défaut qui liait son pont Model Context Protocol au port 3001 sur toutes les interfaces réseau et n'exigeait aucune authentification. Une seule requête HTTP vers le point de terminaison /mcp suffisait pour exécuter des commandes shell dans le conteneur sous le compte node, et depuis ce shell, un attaquant pouvait lire chaque clé API de fournisseur LLM dans l'environnement, exfiltrer toutes les conversations stockées, lancer des essaims d'agents facturés sur les clés de la victime et écrire des instructions dans le stockage d'apprentissage AgentDB qui continuerait d'orienter les réponses de l'IA pour chaque utilisateur de cette instance. La faille est CVE-2026-59726, surnommée RufRoot, notée CVSS 10.0 et corrigée dans 3.16.3 en moins de 24 heures. Aucune des sources que nous avons consultées ne signale d'exploitation en conditions réelles. La fiche que l'archive consacre à cette faille, avec sa liste de sources et son niveau de confiance, se trouve dans l'archive des incidents d'IA, aux côtés des autres entrées MCP et d'infrastructure répertoriées autour d'elle.

Le défaut qui a ouvert la porte

Ruflo est ce que Noma Labs appelle un méta-harnais d’agents : une couche auto-hébergée pour des essaims pouvant compter jusqu’à 100 agents, conçue pour Claude Code et OpenAI Codex, avec une mémoire à long terme et un serveur MCP intégré pour l’appel d’outils. L’installation habituelle est celle décrite par Noma — cloner le dépôt, ajouter les clés d’API LLM, exécuter docker compose up -d. Le fichier compose par défaut du projet liait le bridge à 0.0.0.0 sur le port 3001 et liait son MongoDB associé à toutes les interfaces sur 27017, avec l’authentification Mongo désactivée. La portée de cette exposition dépend du pare-feu, du groupe de sécurité et de la segmentation réseau autour du déploiement ; selon la propre formulation de Noma, toute instance joignable sur le réseau était entièrement exploitable sans authentification.

Le pont MCP est un serveur Express.js qui transforme les requêtes JSON-RPC arrivant via HTTP en appels à la bibliothèque d’outils interne de Ruflo. Dans la ligne vulnérable, cette bibliothèque contenait 233 outils couvrant l’accès au shell, les opérations sur base de données, la gestion des agents et le stockage en mémoire. L’analyse de Noma dit sans détour ce que cela en fait : « Le MCP Bridge n’est pas une interface de débogage auxiliaire quelconque ; c’est plutôt le système nerveux central de Ruflo. Chaque appel d’outil, chaque action d’agent, chaque opération de mémoire passe par le MCP Bridge. »

La couche d’authentification était absente, et pas seulement faible. POST /mcp et POST /mcp/:group implémentent le protocole MCP JSON-RPC et transmettent les requêtes entrantes tools/call directement à la fonction interne executeTool(). Ruflo maintenait bien une liste de blocage de commandes, AUTOPILOT_BLOCKED_PATTERNS, destinée à arrêter les opérations shell dangereuses, mais l’avis GitHub indique explicitement qu’elle « n’était appliquée que dans le flux autopilot. POST /mcp et POST /mcp/:group la contournaient. » Un appel non authentifié nommant ruflo__terminal_execute a donc exécuté des commandes arbitraires en tant qu’utilisateur node du conteneur, enregistré dans l’avis sous uid 1000, sans qu’aucun jeton, aucune session ni aucune interaction préalable ne soit nécessaire. Aucune élévation de privilèges n’était nécessaire pour ce qui allait suivre : les identifiants, les API des agents, le stockage mémoire et la base de données étaient tous accessibles depuis ce shell.

La chaîne, en huit étapes

Noma Labs a publié une preuve de concept en huit étapes et a confirmé chaque étape en conditions réelles contre un déploiement Ruflo par défaut sur AWS EC2, en utilisant des callbacks hors bande vers Burp Collaborator pour vérifier l’exécution et l’exfiltration. Les étapes ne sont pas des conséquences spéculatives empilées sur une faille théorique ; elles constituent une séquence démontrée, et la section de remédiation de l’avis est rédigée pour les opérateurs qui pourraient déjà se trouver dans cette situation.

A hand-built HTML card headed 'The RufRoot chain, step by step', kicker 'RufRoot - CVE-2026-59726', subtitle 'Eight steps Noma Labs confirmed live against a default Ruflo deployment on AWS EC2'. Eight rows, each with a circled step number, the action, and what it yielded. 1, call tools/list: enumerate the 233 exposed tools. 2, call ruflo__terminal_execute over POST /mcp: shell inside the bridge container as node, uid 1000, verified by an out-of-band callback. 3, run printenv: every LLM provider API key in the container environment. 4, call ruflo__swarm_init and ruflo__agent_spawn: attacker-controlled agent swarms running on the victim's keys and compute. 5, call ruflo__agentdb_pattern-store: a fabricated SOC2 policy in the learning store, steering future output for all users. 6, query MongoDB on port 27017: every stored conversation, title and metadata. 7, write /app/beacon.js and inject it into index.js, then kill PID 1: a backdoor that survives the container restart policy. 8, clear shell history: no forensic trace. A footer cites Noma Labs and CVE-2026-59726, and marks step 5 as the step a patched redeploy does not undo.

La reconnaissance intervient en premier car la liste des outils est publique pour tout appelant : tools/list renvoie toutes les 233 entrées sans identifiant. L'exécution de commandes découle du même point de terminaison, et la preuve de concept de Noma a confirmé ce point au moyen d'un rappel hors bande plutôt que par une déduction à partir d'un corps de réponse. L'étape liée à l'environnement est celle qui transforme la compromission d'un hôte en problème de facturation et de perte de données : le fichier compose a transmis les clés du fournisseur dans le conteneur et les processus backend ont hérité de l'environnement complet via { ...process.env }, si bien qu'un seul printenv les a déversées. Ces clés ont ensuite permis à ruflo__swarm_init et ruflo__agent_spawn de lancer des essaims contrôlés par l'attaquant qui ont consommé le quota et la puissance de calcul de la victime.

L’étape cinq est celle qui survit à l’intrusion. En utilisant ruflo__agentdb_pattern-store, Noma a implanté une politique de conformité SOC2 fabriquée qui ordonnait à l’IA d’intégrer une URL contrôlée par l’attaquant dans chaque script de déploiement qu’elle générait ensuite. Comme AgentDB a précisément pour vocation de conserver exactement ce type de motif afin que le comportement des agents s’améliore d’une session à l’autre, l’entrée injectée s’est comportée comme le système était conçu pour le faire. « Les conversations futures produisent une sortie influencée par l’attaquant, et l’utilisateur n’a aucun moyen de le savoir », indique le compte rendu de Noma. Dark Reading, qui a signalé la faille le 29 juillet, a soumis cette distinction à un expert externe : John Gallagher, vice-président chez Viakoo, a déclaré qu’il s’agissait d’une autre catégorie de problème : « Il ne s’agit pas d’exploiter des failles logicielles, il s’agit d’un empoisonnement de la mémoire. »

Les étapes restantes relèvent de la post-exploitation classique, exécutées avec une facilité inhabituelle. MongoDB sur le réseau Docker interne n’avait aucune authentification, les attaquants ont donc installé un client dans /tmp, ont vidé les conversations et les ont exfiltrées. La persistance a été obtenue en écrivant /app/beacon.js, en ajoutant une require('./beacon.js') ligne à index.js et en tuant le PID 1, après quoi la politique de redémarrage de Docker a ramené le conteneur avec le beacon actif. La dernière étape consistait à effacer l’historique du shell. Tout cela découlait d’une seule requête non authentifiée ; rien dans le déploiement par défaut n’a arrêté la chaîne à un quelconque moment.

Le correctif, et la partie qu’un patch ne peut atteindre

Le mainteneur a livré Ruflo 3.16.3 dans les 24 heures suivant la divulgation du 30 juin, et GitHub a publié l'avis GHSA-c4hm-4h84-2cf3 le 1er juillet 2026. La remédiation, suivie dans le dépôt sous ADR-166 et fusionnée dans la pull request 2521, modifie les valeurs par défaut plutôt que de simplement ajouter une vérification :

• Le pont MCP se lie à 127.0.0.1 par défaut ; une exposition publique requiert MCP_AUTH_TOKEN, et le processus s'arrête avec un message fatal au démarrage si le jeton est absent.

• Un intergiciel de jeton Bearer avec une comparaison à temps constant se trouve devant le pont.

• terminal_execute est conditionné côté serveur par MCP_ENABLE_TERMINAL=true, désactivé par défaut, et la restriction est appliquée de manière identique sur /mcp, /mcp/:group et le flux autopilote — le maillon manquant qui avait permis à la chaîne divulguée d’atteindre un shell.

• MongoDB nécessite une authentification, avec un mot de passe root requis pour que le conteneur démarre.

• Le conteneur bridge s’exécute avec un système de fichiers racine en lecture seule et tmpfs, une liste d’autorisation CORS remplace le joker, et des verrous de régression CI couvrent la configuration statique ainsi que le comportement à l’exécution.

Rien de tout cela ne supprime une entrée déjà écrite dans AgentDB. L’avis demande aux opérateurs de toute instance exposée de bloquer immédiatement les ports 3001 et 27017 au niveau du pare-feu, de renouveler les clés du fournisseur, d’auditer le magasin de motifs afin d’y trouver les entrées agentdb_pattern-store injectées et de les purger, d’auditer MongoDB pour détecter toute altération, et de reconstruire les conteneurs à partir d’images propres. Son avertissement est inhabituellement direct : « Un redéploiement corrigé seul ne permet PAS d’annuler l’empoisonnement. » L’analyse de Noma aboutit à la même conclusion — l’audit du magasin de motifs fait partie de la remédiation, et le correctif n’en fait pas partie.

La raison est structurelle plutôt qu’une simple négligence. Un stock de motifs qui façonne la sortie future du modèle n’a aucun moyen de distinguer une instruction légitime apprise d’une instruction implantée par un attaquant ; les deux arrivent sous forme de motifs, et le système est conçu pour leur faire confiance. Cela transforme une vulnérabilité réseau en un problème d’intégrité des données avec un arriéré inconnu, et cela signifie que, pour un opérateur, la question n’est pas seulement de savoir si le port était joignable, mais ce que la mémoire contient désormais.

A hand-built HTML timeline card headed 'From disclosure to coverage', kicker 'RufRoot - disclosure timeline', subtitle 'Five dates, from the private report to the day the record was filed'. Six dated rows. 30 June 2026: Noma Labs discloses the flaw to Ruflo's maintainer with a working proof of concept. 1 July 2026: Ruflo 3.16.3 ships and GHSA-c4hm-4h84-2cf3 is published with CVSS 10.0. 9 July 2026: the CVE record for CVE-2026-59726 is published in the National Vulnerability Database, later marked Deferred. 29 July 2026: The Hacker News and Dark Reading report the flaw; Noma Labs publishes its research. 30 July 2026: SecurityWeek reports it, and the record is dated to this day. A separate amber note reads: no source we read reports exploitation in the wild at any point on this timeline. A footer cites The Hacker News, the GitHub advisory API, the NVD record and SecurityWeek.

Ce qui est établi, et ce qui ne l’est pas

Le dossier technique présenté ici est primordial et cohérent. L'avis nomme le paquet affecté (npm ruflo en dessous de 3.16.3), la version corrigée, les points de terminaison exacts, l'utilisateur du conteneur et les deux ports ; les notes de version du mainteneur décrivent la même chaîne ; l'analyse de Noma fournit le détail de la preuve de concept ; et des médias indépendants — The Hacker News et Dark Reading le 29 juillet 2026, SecurityWeek le 30 juillet — rapportent le même mécanisme sans ajouter de précisions contradictoires. Une note de recherche de la Cloud Security Alliance datée du 5 août 2026 reprend la séquence et la place aux côtés de vulnérabilités antérieures non authentifiées liées à MCP et à l'orchestration d'agents qu'elle avait suivies. L'archive attribue au dossier un niveau de confiance A, incontesté, et le qualifie de jalon ; sa note de gravité pour le dossier est élevée, distincte du score CVSS lui-même.

Un détail de notation mérite d’être énoncé avec précision, car deux documents principaux divergent. GHSA-c4hm-4h84-2cf3 porte CVSS 10.0 sur le vecteur AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H — c’est le changement de portée qui le fait atteindre le maximum. La copie de ce même avis sur la page de publication de la version 3.16.3 décrit la note comme « CVSS 9.8 critique ». L’entrée du National Vulnerability Database, consultée le 30 octobre 2026, publiée le 9 juillet 2026 et marquée Deferred, porte le 10.0 de GitHub comme métrique secondaire et aucune note propre rédigée par la NVD. Nous n’avons pas trouvé d’explication pour la mention 9.8, et cet article ne la résout pas.

Les chiffres d’adoption sont plus minces. Noma Labs décrivait alors le projet comme ayant plus de 67 000 étoiles GitHub, environ 10 millions de téléchargements et environ 1 million d’utilisateurs actifs ; The Hacker News, comme l’a résumé la Cloud Security Alliance, citait plus de 66 500 étoiles. Les décomptes de téléchargements et d’utilisateurs sont ceux de Noma et ne sont pas vérifiés indépendamment. Le nombre d’étoiles du dépôt au 30 octobre 2026 était de 80 198 — un contrôle de la portée du projet, non une mesure de l’exposition, et aucune source n’a publié combien de déploiements étaient réellement joignables depuis un réseau.

A hand-built HTML card headed 'Established, and not', kicker 'RufRoot - evidence', subtitle 'What the advisory, the release notes and the research support, against what nobody has shown'. Two columns. The left column, headed 'Established', lists: CVE-2026-59726 affects npm ruflo below 3.16.3 and is patched in 3.16.3; the default compose file bound the MCP bridge to 0.0.0.0 on port 3001 and MongoDB to 27017 without authentication; POST /mcp and POST /mcp/:group reached 233 tools and terminal_execute with no credential, running as node, uid 1000; Noma Labs confirmed an eight-step chain live on AWS EC2 with out-of-band callbacks, including an AgentDB pattern injection; the advisory was published 1 July 2026 with CVSS 10.0 and the fix shipped within 24 hours of the 30 June disclosure; the archive record is confidence A, undisputed, no real harm recorded, GLOBAL, MCP and INFRA. The right column, headed 'Not established', lists: that anyone exploited the flaw in the wild; how many Ruflo deployments were actually network-reachable; that any instance's AgentDB was poisoned, since no audit results are published; whether 9.8 in the release notes or 10.0 in the advisory is the intended score; Noma's counts of 10M downloads and 1M active users, which no independent source confirms; that any operator received notice beyond the public advisory. A footer cites GHSA-c4hm-4h84-2cf3, the Ruflo 3.16.3 release notes, Noma Labs and SecurityWeek.

Ce qui n’est pas établi est la partie la plus facile à gonfler. Ni Noma, ni le mainteneur, ni aucun média que nous avons lu n’affirme que la faille a été exploitée dans la nature, et les archives ne consignent aucun préjudice réel pour cette entrée. La preuve de concept de Noma a été exécutée contre un déploiement que les chercheurs contrôlaient sur EC2, ce qui montre ce qu’une installation par défaut permet, et non ce qu’a subi une instance de production particulière. Aucun décompte des déploiements Ruflo exposés à Internet n’a été publié, et aucun opérateur n’a publié les résultats d’un audit AgentDB ; la population touchée et la quantité de mémoire empoisonnée existante sont donc toutes deux inconnues. La recommandation de la Cloud Security Alliance de considérer les déploiements exposés comme totalement compromis est une posture de risque justifiée par une exploitation sans interaction ; ce n’est pas la preuve qu’une intrusion a eu lieu.

Pourquoi l'archive y voit un jalon

Le flag emblématique de l’archive repose sur quelque chose de plus précis qu’un score maximal. Une faille d’exécution de code à distance de CVSS 10.0 dans un service auto-hébergé populaire est un genre familier, et la correction est ici arrivée en moins de 24 heures. Ce qui distingue RufRoot, c’est que l’exploit s’est terminé dans le propre pipeline d’apprentissage de l’IA, où le remède conventionnel — mettre à niveau, redéployer, fermer le port — laisse l’instruction injectée en place et continuant de façonner les réponses. La note de la Cloud Security Alliance y voit une étude de cas d’un schéma qu’elle avait déjà documenté dans l’outillage des agents, notamment une chaîne de checkpoints LangGraph et une surface AutoGen Studio non authentifiée, et soutient que la mémoire persistante utilisée pour façonner le comportement des agents devrait être traitée comme un stockage de données sensibles sous surveillance d’intégrité plutôt que comme un cache.

Ce recadrage constitue la conclusion pratique à retenir. L’artefact visible de cet incident est une dépendance corrigée et un avis publié ; l’artefact invisible est tout motif que l’AgentDB d’un opérateur peut encore contenir, et il n’existe aucun outil ni aucune signature publiés permettant de distinguer une politique apprise légitime d’une politique implantée. Pour les organisations qui ont exécuté le fichier compose par défaut derrière un réseau accessible, la réponse utile est celle que donnent à la fois Noma et l’avis : partir du principe que les clés ont été lues, les renouveler et inspecter la mémoire avant de faire confiance aux réponses qu’elle produit.