Illustration technique abstraite sur un fond bleu marine foncé : un port d’entrée lumineux sur la paroi gauche d’une boîte translucide laisse passer une seule fine ligne pâle ne portant ni badge ni clé. À l’intérieur de la boîte, la ligne rencontre une pile de petits panneaux arrondis et se ramifie en une courte rangée de traits anguleux évoquant des commandes, qui descendent dans un compartiment inférieur contenant un groupe de jetons lumineux en forme de clé. Une seconde porte fermée, soulignée en bleu pâle, se trouve à côté du port d’entrée. Aucun texte, aucune personne, aucun logo d’organisation.
AI Safety Incidents

Une requête POST non authentifiée à l'API de gestion de Bifrost a lancé une commande en tant qu'utilisateur de la passerelle

Auteur

Magnus Corvin

Date de publication

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

Le 14 septembre 2026, JFrog Security Research a publié CVE-2026-90898, une faille d'exécution de commandes dans Bifrost — la passerelle IA open source maintenue par Maxim — et l'a notée 9,8 sur 10. L'attaque consistait en une seule requête HTTP. Un POST /api/mcp/client non authentifié enregistre un client MCP stdio, que Bifrost interprète comme un nom de programme suivi d'une liste d'arguments et démarre immédiatement, avant tout handshake MCP, sous l'utilisateur qui exécute la passerelle ; sur l'image officielle, cet utilisateur est appuser. Une passerelle est aussi l'endroit où résident les clés de fournisseur et les clés virtuelles d'un opérateur, de sorte qu'un processus qui y tourne accède aux identifiants. Ce qui rendait la requête anonyme était un paramètre par défaut livré ainsi : governance.auth_config.is_enabled est false, et, selon la formulation de JFrog, « l'authentification désactivée signifie que chaque appelant est un administrateur local ». Aucune source citée ne rapporte que la faille ait été exploitée. Les sources propres au dossier, la note de gravité et l'évaluation de confiance figurent dans les archives des incidents d'IA, aux côtés des autres vulnérabilités documentées des passerelles IA et de MCP de l'année écoulée.

Bifrost est un service unique qui se situe entre une application et les modèles de langage qu’elle appelle. Son dépôt le décrit comme une passerelle open source, sous licence Apache-2.0, qui « unifie l’accès à 23+ fournisseurs ... via une API unique compatible OpenAI », avec notamment le proxy MCP, des clés virtuelles, des budgets et l’équilibrage de charge par fournisseur parmi ses fonctionnalités ; elle totalisait environ 9 150 étoiles GitHub le 28 octobre 2026. Cette combinaison est précisément la raison pour laquelle un shell sur cet hôte particulier mérite un score critique : le processus qui achemine les requêtes est aussi celui qui détient les identifiants de chaque service en amont vers lequel il les achemine. The Hacker News, en relatant la faille le 22 septembre, a formulé la conséquence sans détour : comme la passerelle stocke les clés API de chaque fournisseur connecté, l’exécution de commandes sur le processus de la passerelle donne à l’attaquant accès à ces identifiants.

Toute l’attaque tenait en un seul POST

L'avis de JFrog, publié sous la référence JFSA-2026-001686326, décrit un endpoint qui existe pour permettre à un administrateur d'ajouter un serveur MCP. Un client stdio dans la spécification MCP n'est pas une URL ; c'est une commande et ses arguments, que le programme hôte est censé lancer. Bifrost accepte cette configuration via l'API de gestion et démarre le programme dès que le client est ajouté. Aucune négociation de capacités, aucune vérification de l'identité du serveur, aucune poignée de main n'a besoin d'aboutir au préalable — l'avis précise explicitement que ce n'est pas la poignée de main qui déclenche le processus.

La preuve de concept dans l’avis est un court corps JSON envoyé à une passerelle dont l’authentification est laissée à sa valeur par défaut. Il enregistre un client dont la commande est /bin/sh et dont les arguments écrivent un fichier marqueur, puis relit le marqueur. JFrog ajoute un détail opérationnel qui compte lors de la réponse aux incidents : la requête HTTP peut expirer pendant que Bifrost attend une poignée de main qui ne vient jamais, et une expiration dans les journaux ne signifie pas que la commande n’a pas démarré. Le processus est déjà en cours d’exécution à ce moment-là.

L'enregistrement CVE, attribué par JFrog en tant qu'autorité de numérotation CVE, associe le défaut à CWE-306, absence d'authentification pour une fonction critique, et CWE-284, contrôle d'accès inapproprié, et nomme les deux fichiers qui contiennent le gestionnaire et son middleware : transports/bifrost-http/handlers/mcp.go et transports/bifrost-http/handlers/middlewares.go. Le module affecté est github.com/maximhq/bifrost/transports, sous Linux et macOS, pour toutes les versions antérieures à 2.1.0.

La valeur par défaut qui a rendu la requête anonyme

Rien de tout cela ne serait exploitable sans une seconde condition, et cette seconde condition est la valeur par défaut du produit lui-même. Le middleware d’authentification de gestion de Bifrost est conditionné par governance.auth_config.is_enabled, qui est livré à false. Lorsqu’il est désactivé, le middleware traite chaque appelant comme un administrateur local — le résumé de l’avis indique lui-même que l’authentification désactivée signifie que chaque appelant est un administrateur local — de sorte que le contrôle qui aurait bloqué la requête n’était jamais dans le chemin d’exécution.

Le correctif, la pull request #6757, a été fusionnée dans le projet le 2 septembre 2026, douze jours avant la publication de la CVE et six jours avant la version qui l'intègre. Sa description est d'une franchise inhabituelle quant à la nature du problème : la pull request ferme deux primitives d'accès non authentifié au sein de la même API d'enregistrement, et non une seule. La première est le client stdio. La seconde est un client MCP http ou sse pointant vers une adresse de bouclage ou de réseau privé, ce que la demande qualifie de primitive SSRF contre des services internes, « y compris le point de terminaison de métadonnées cloud 169.254.169.254 ». Les deux sont désormais refusées avec un HTTP 403 lorsque la requête atteint le gestionnaire sans véritable contrôle des identifiants. Les administrateurs authentifiés conservent les deux capacités, et les opérateurs peuvent toujours provisionner des clients stdio via config.json.

Cette seconde primitive n’apparaît pas dans le texte de la CVE. Elle se trouve dans le correctif, et c’est la raison pour laquelle le correctif est un garde-fou plutôt qu’une suppression : la fonctionnalité n’a jamais été le bug. C’était le fait de traiter un appelant non authentifié comme un administrateur.

Là où l’API de gestion écoutait réellement

La joignabilité est la partie du dossier que les sources décrivent avec le plus de constance, et qu’elles mesurent le moins. L’avis de JFrog indique que le binaire standard utilise par défaut localhost:8080, ce qui confine un attaquant à l’hôte lui-même. L’avis de la même équipe sur la faille complémentaire indique que l’image Docker officielle se lie à 0.0.0.0 à la place, et The Hacker News le répète : l’image rend l’API de gestion joignable depuis l’extérieur du conteneur si le port est publié. Le démarrage rapide du README de Bifrost lui-même exécute « docker run -p 8080:8080 maximhq/bifrost », et l’option -p de Docker publie un port de conteneur sur toutes les interfaces de l’hôte sauf si une adresse est indiquée. L’avis GitHub sur la faille consigne que le chemin signalé a été vérifié sur l’image publiée maximhq/bifrost:latest, où une requête non authentifiée lançait /bin/sh et écrivait un marqueur en tant qu’appuser.

Ce qu’aucune source ne fournit, c’est un chiffre. Il n’existe aucun décompte des instances Bifrost exposées à Internet, aucun résultat de scan, aucune télémétrie et aucune victime. Le bilan ne fait état d’aucun préjudice réel, et cette absence est un fait concernant la couverture médiatique plutôt qu’une garantie rassurante — cette classe de vulnérabilité n’est dangereuse que lorsque deux conditions se conjuguent, un écouteur exposé et une configuration par défaut laissée intacte, et personne n’a publié à quelle fréquence elles l’ont été.

Un seul point de terminaison, deux failles, une seule cause racine

Huit jours avant l'avis MCP, le 6 septembre 2026, la même équipe de recherche a publié CVE-2026-86242, un second chemin non authentifié vers le même processus. Découverte par Or Peles et notée 8,1, elle exploite POST /api/plugins : Bifrost accepte un plugin personnalisé activé dont le chemin est une URL HTTP, le chargeur d'objets partagés traite un chemin préfixé par http comme un téléchargement, écrit le corps dans un fichier .so temporaire et transmet ce fichier à plugin.Open de Go. Si le chargement réussit, une fonction Init facultative s'exécute immédiatement avec la configuration de l'attaquant, en tant qu'utilisateur de la passerelle.

La variante de compilation détermine ce qu’obtient un attaquant. Sur les builds liés dynamiquement que le projet documente pour les plugins Go personnalisés, le chargement est censé réussir, ce que l’avis qualifie d’exécution de code à distance non authentifiée. Sur l’image Docker liée statiquement publiée, plugin.Open échoue avec « Dynamic loading not supported », de sorte que la même requête se dégrade en falsification de requête côté serveur. Un plugin chargeable doit également correspondre à la version de Go, au système d’exploitation, à l’architecture et au mode de liaison de l’hôte, ce qui est une véritable contrainte et non une formalité. Le correctif est transports/v2.0.0, via la pull request #5763, et la plage affectée correspond à tout ce qui se trouve en dessous.

Le piège, c’est l’ordre des publications. La v2.0.0 corrige le chemin du plugin et ne corrige pas celui de MCP — The Hacker News le dit explicitement, et l’avis MCP de JFrog confirme que la v2.0.0 permet encore l’enregistrement stdio non authentifié. Un opérateur qui avait appliqué le correctif rapidement après l’avis du 6 septembre était, sur ce seul point, encore exposé jusqu’à la publication de transports/v2.1.0 le 8 septembre. Les deux failles se résument à la même phrase dans les deux avis et dans la pull request du fournisseur lui-même : l’authentification désactivée est la valeur par défaut.

Ce que les sources établissent, et ce qu’elles n’établissent pas

Le mécanisme, la plage de versions, le correctif et le crédit font consensus dans l’avis, l’enregistrement CVE attribué par JFrog, la pull request et les notes de version du projet, ainsi que dans The Hacker News. Le désaccord restant porte sur la gravité, et il est réel.

Le 9,8 est le score de JFrog en tant que CNA assignant, et c'est le score que NVD reprend — l'enregistrement NVD indique reefs@jfrog.com comme source de la métrique et, au 28 octobre 2026, affiche toujours l'entrée au statut « Awaiting Analysis », ce qui signifie que NVD a publié le vecteur de l'assignant plutôt que sa propre analyse de celui-ci. L'évaluation SSVC du coordinateur de la CISA jointe au même enregistrement, horodatée du 14 septembre 2026, indique une exploitation « none », un caractère automatisable « yes » et un impact technique « total ». Le modèle EPSS de FIRST estimait la probabilité d'exploitation dans les 30 prochains jours à 2,3 % le 27 octobre 2026, au 83e centile parmi tous les CVE évalués, et ni ce CVE ni CVE-2026-86242 n'apparaissent dans le catalogue Known Exploited Vulnerabilities de la CISA daté du 27 octobre 2026.

En revanche, Maxim conteste cette notation. Dans une déclaration à The Hacker News, Akshay Deo, directeur technique et cofondateur de l’entreprise, a déclaré que le risque pratique est inférieur à ce que suggère le score critique, car l’exploitation exige que l’interface de gestion soit accessible depuis un réseau non fiable et que l’opérateur ait laissé l’authentification non configurée ; le déploiement par défaut et documenté, a-t-il dit, place la passerelle à l’intérieur d’un réseau privé. The Hacker News rapporte que Maxim considère la faille comme de faible gravité et travaille avec JFrog, qui a attribué le CVE, à réviser l’évaluation. Cette déclaration n’apparaît que dans ce seul média ; aucune seconde publication citant la position de Maxim n’a été trouvée, et aucune révision n’avait été publiée au 28 octobre 2026 — l’enregistrement CVE avait été mis à jour pour la dernière fois le 14 septembre et l’entrée NVD le 18 septembre, toutes deux toujours à 9,8.

Deux autres lacunes méritent d’être nommées plutôt que d’être gommées. Les avis GitHub du projet, publiés le 23 septembre 2026 et attribuant le signalement au chercheur de JFrog, affichent toujours 9,8 pour la faille MCP et 8,1 pour la faille du plugin, sans aucune trace de la révision décrite par Maxim. Et les plages concernées dans ces avis sont exprimées par rapport aux versions de modules Go — inférieures à 1.5.27 et inférieures à 1.6.3 — plutôt qu’aux tags de version des transports qu’utilisent les avis JFrog, de sorte qu’un opérateur qui confronte un document à l’autre doit réconcilier deux systèmes de numérotation avant de déterminer si une compilation est affectée.

Rendered two-column card with the kicker 'BIFROST · CVE-2026-90898' and the title 'What the sources establish — and what they do not', subtitled 'Sources: JFrog JFSA-2026-001686326 (14 September 2026), the CVE record assigned by JFrog, NVD, FIRST EPSS, the CISA KEV catalogue, and the project's pull request 6757 and release tags.' The left column, headed 'ESTABLISHED' in blue, lists eight items: score 9.8 with the vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H supplied by the CNA; CWE-306 missing authentication with CWE-284 access control; unauthenticated POST /api/mcp/client starting a program as the gateway user; affected below transports 2.1.0 when management auth is disabled, the default; fixed in transports 2.1.0, PR 6757 merged 2 September, released 8 September 2026; CISA SSVC of 14 September 2026 with exploitation none, automatable yes, impact total; EPSS 2.3 percent at the 83rd percentile on 27 October 2026; and not in the CISA KEV catalogue dated 27 October 2026. The right column, headed 'NOT ESTABLISHED, OR CONTESTED' in amber, lists eight items: no exploitation reported, and the archive records real harm as false; no count of exposed or affected deployments in any cited source; severity contested, the vendor's CTO telling The Hacker News it is low; no published revision to the score as of 28 October 2026; NVD still Awaiting Analysis, so the 9.8 is the assigner's and not NVD's; affected ranges stated in two numbering schemes across the advisories; no detection or triage guidance in either advisory; and no evidence tying the flaw to any intrusion. A footer reads 'Card rendered by OrcaRouter'. The OrcaRouter logo is composited in the bottom-right corner.

Ce qui a changé, et ce qu’on a demandé aux opérateurs de faire

La chronologie remonte à rebours depuis la divulgation, qui constitue l’élément le moins intuitif du dossier. La pull request #6757 a été fusionnée le 2 septembre 2026. transports/v2.1.0, la version que l’avis désigne comme la première à contenir le correctif, a été publiée le 8 septembre — six jours avant la publication de CVE-2026-90898, le 14 septembre. Le correctif du plugin du projet a précédé son propre avis de la même manière : transports/v2.0.0 est sortie le 26 août, onze jours avant la CVE du 6 septembre. Les opérateurs qui suivent les versions plutôt que les flux d’informations disposaient des correctifs avant les avertissements.

Ce que la mise à niveau apporte est limité et précis. Sur 2.1.0 et versions ultérieures, une inscription stdio non authentifiée renvoie 403 et la commande ne démarre pas ; les administrateurs authentifiés conservent cette capacité, et le provisionnement via config.json fonctionne toujours. Le palliatif de l’avis pour toute personne qui ne peut pas migrer consiste à définir governance.auth_config.is_enabled sur true, à utiliser des identifiants administrateur forts et à maintenir l’écouteur de gestion hors des réseaux non fiables. L’instruction de JFrog pour les instances qui ont tourné exposées avec l’authentification désactivée n’est pas d’enquêter en espérant : traitez-les comme compromises et procédez à la rotation des clés virtuelles et des identifiants des fournisseurs.

L'arithmétique des versions est l'élément le plus susceptible d'être mal interprété. v2.0.0 ne corrige pas ce défaut. La série 1.6.x jusqu'à la version 1.6.11 ne contient pas non plus le correctif, et l'étiquette la plus récente de la série 1.6 du projet est transports/v1.6.11, publiée le 14 août 2026, sans aucune série 1.7 dans le dépôt à la date du 28 octobre 2026 — il n'existe donc aucune version corrigée au sein de cette série, et un exploitant en 1.6.x doit passer à 2.1.0 ou une version ultérieure plutôt que d'attendre un rétroportage. La ligne de publication actuelle est bien au-delà de ce point : transports/v2.3.2 a été publiée le 20 octobre 2026.

Rendered release-timeline card with the kicker 'BIFROST · RELEASE TIMELINE', the title 'The fix shipped six days before the advisory' and the subtitle 'Dates from the project's release tags and pull request metadata, the JFrog advisories and The Hacker News. Every date below is a single day.' A horizontal timeline carries seven dated markers: 26 Aug 2026, transports 2.0.0 published; 2 Sep 2026, PR 6757 merged; 6 Sep 2026, CVE-2026-86242 published, 8.1; 8 Sep 2026, transports 2.1.0, the fix; 14 Sep 2026, CVE-2026-90898 published, 9.8; 22 Sep 2026, vendor statement reported; 23 Sep 2026, GitHub advisories published. Below it, three bars show release lines: a hatched amber bar labelled '1.6.x line' reading 'affected — no patched release in the series; newest tag 1.6.11, 14 August 2026'; a bar labelled '2.0.0' split between a pale blue segment reading 'plugin flaw fixed' and a red segment reading 'MCP flaw still open'; and a solid blue bar labelled '2.1.0 and later' reading 'MCP fix — unauthenticated stdio registration returns 403'. A foot band headed 'THE PART THAT IS EASY TO GET WRONG' notes that 2.0.0 was the patched release on 6 September and did not close the MCP path, that for the two days before 2.1.0 an operator who patched on the day of the warning was still exposed, and that the 1.6.x series has no fixed release at all. A footer reads 'Card rendered by OrcaRouter'. The OrcaRouter logo is composited in the bottom-right corner.

Deux choses se sont produites après la publication dont un défenseur peut tirer parti. Les avis de sécurité GitHub du projet — GHSA-86gf-xh3g-rvxq pour la faille MCP de gravité critique et GHSA-2qp8-4xgm-fw6g pour la faille du plugin de gravité élevée — ont été publiés le 23 septembre 2026, ce qui donne aux flux de dépendances et d’avis un second identifiant à utiliser pour la mise en correspondance. Et le 13 octobre 2026, un modèle de détection pour CVE-2026-90898 a été fusionné dans le dépôt public nuclei-templates, ce qui signifie que le contrôle d’une tentative d’inscription non authentifiée peut désormais être exécuté par un pipeline d’analyse au lieu de devoir être écrit par chaque équipe.

Abstract illustration on a dark navy field: a wide translucent slab representing a credential store, filled with evenly spaced glowing key-shaped tokens, stands behind a low wall. A narrow gateway aperture in the wall holds a fresh, brighter set of tokens, a blank card-shaped slip lies pushed aside on the ground in front of it, and a thin amber arc sweeps from the aperture around to the store. A second gateway aperture in the background remains closed and unlit. No text, no people, no organisation logos.

Ce qui reste en suspens, c’est la gravité, et elle sera tranchée ou non par les deux organisations qui en discutent encore. La position de Maxim, telle que rapportée, est qu’un 9.8 surévalue une faille qui nécessite un listener exposé et une valeur par défaut non définie pour avoir de l’importance — une affirmation testable en principe et non testée publiquement, car personne n’a publié combien de déploiements remplissaient ces deux conditions. La position de JFrog est le score qu’elle a attribué en tant que CNA, toujours en vigueur dans l’enregistrement CVE et dans le NVD. Tant que l’une d’elles ne publie pas de révision, la lecture opérationnelle est la même dans les deux cas : la faille est réelle et a été vérifiée de bout en bout sur l’image de conteneur publiée, le correctif a été livré avant l’avertissement, la série 1.6 n’a aucun correctif du tout, et toute instance qui a tourné avec l’authentification désactivée sur un listener joignable doit être traitée comme un incident d’identifiants plutôt que comme un correctif à planifier.