La page web qui s'appelait localhost
Le 17 août, la CISA a ajouté la CVE-2025-62593 de Ray à son catalogue des vulnérabilités activement exploitées. Il s'agit d'une exécution de code à distance sur la machine du développeur lui-même, déclenchée par une page web qu'il a visitée, atteignant un service qui n'écoutait que sur 127.0.0.1. Les agents de code remplissent cette interface de serveurs non authentifiés depuis un an. Bromure Agentic Coding les place là où aucun onglet de navigateur ne peut composer le numéro.
Votre serveur de développement, votre notebook et votre tableau de bord local
lient chacun un port non authentifié à 127.0.0.1, au motif que seule la
personne au clavier peut l'atteindre. Une page web le peut aussi. La politique
de navigateur qui s'y oppose tombe en une soixantaine de secondes.
Le 17 août, la CISA a ajouté une vulnérabilité à son catalogue des vulnérabilités activement exploitées et a donné aux agences fédérales jusqu'au 20 août pour la traiter. The Hacker News en a fait le compte rendu le lendemain matin. L'entrée est la CVE-2025-62593, une faille d'injection de code dans Ray, le framework de calcul distribué qu'une large part de l'industrie utilise pour entraîner et servir des modèles.
Le code s'exécute sur le portable du développeur lui-même. Quelqu'un faisait tourner Ray en local pour son travail, a ouvert une page web, et la page a exécuté une commande à sa place.
Trois faits ordinaires, mal agencés
L'avis de sécurité le note 9,4 et décrit une chaîne dont aucune étape n'a rien d'exotique.
L'API Jobs de Ray prend une commande shell. C'est le produit : vous faites un
POST sur /api/jobs/ avec un point d'entrée et le cluster exécute la
commande. Le tableau de bord écoute sur le port 8265, et ni lui ni les points
d'entrée de jobs n'exigent d'authentification. C'était un choix délibéré des
mainteneurs, dont la position est que
la sécurité et l'isolation doivent être imposées en dehors du cluster Ray.
Vous le placez quelque part où seuls des appelants de confiance peuvent
l'atteindre, et l'interface de loopback d'un portable ressemble à un tel
endroit.
Ray disposait bien d'un garde-fou contre les navigateurs. Il vérifiait si
l'en-tête User-Agent commençait par Mozilla et refusait ces requêtes, en
supposant qu'un navigateur ne peut pas mentir sur son propre User-Agent. Dans
Firefox et Safari, l'API Fetch permet à une page de fixer cet en-tête à ce que
bon lui semble.
Reste la politique d'origine unique, le seul contrôle qui se dresse entre une
page sur internet et un service sur votre machine. Le DNS rebinding la démonte.
L'attaquant sert la page depuis un domaine dont il contrôle les enregistrements
DNS, avec une durée de vie très courte. La page se charge, puis résout à nouveau
ce même domaine vers 127.0.0.1. Le navigateur continue de la traiter comme la
même origine, même nom d'hôte et même port, alors que chaque requête part
désormais vers votre machine. La preuve de concept publiée utilise
singularity, un framework de
rebinding public depuis des années.
Enchaînez le tout et une page malveillante, ou une publicité vérolée sur une
page à laquelle vous faisiez confiance, poste un job à votre instance Ray avec
une commande shell dedans. Chrome est protégé par un bug sans rapport ; Firefox
et Safari ne le sont pas. Avi Lumelsky, chez Oligo, a théorisé le contournement
du User-Agent ; Jonathan Leitschuh, alors chez Socket, a construit la chaîne
de rebinding et la divulgation. Ray 2.52.0 corrige la faille et ajoute une
authentification par jeton optionnelle, désactivée par défaut.
Les attaquants sont arrivés avant l'avis de sécurité. Le botnet RondoDox a ramassé la faille deux jours avant la divulgation publique, et la recherche ShadowRay 2.0 d'Oligo suit une campagne qui transforme les déploiements Ray en botnet auto-propagateur : les clusters compromis scannent à la recherche d'autres instances Ray et les infectent, une tâche cron tire de nouvelles charges depuis des dépôts contrôlés par l'attaquant toutes les quinze minutes, et la charge mine du Monero, ouvre des shells inversés et prend ce qu'elle trouve. Oligo a compté plus de 230 000 serveurs Ray exposés, une multiplication par dix depuis son premier rapport en 2024. Un seul cluster compromis a livré 240 Go de code source, de modèles et de jeux de données.
Ce qui écoute sur votre machine
Laissez Ray de côté et comptez autre chose : les serveurs HTTP non authentifiés liés à votre interface de loopback en ce moment même, et combien d'entre eux vous sauriez nommer sans regarder.
Lancez lsof -iTCP -sTCP:LISTEN -P après une semaine de travail et la liste est
plus longue que vous ne l'imaginez. Vite sur 5173. Un service FastAPI sous
uvicorn --reload sur 8000. Jupyter sur 8888, mlflow ui sur 5000, le port
publié d'un conteneur Postgres, un serveur MCP sur 3000, un port de débogage
laissé ouvert par un test qui a planté. Aucun d'eux ne demande qui vous êtes.
Leurs auteurs ont fait l'hypothèse qu'ont faite les mainteneurs de Ray, et elle
tient jusqu'au moment où un onglet dans une autre fenêtre se met à émettre des
requêtes.
Les agents ont changé le volume. Un agent qui traite un ticket démarre des services comme allant de soi. Il lance le serveur de développement pour vérifier sa propre modification, monte une base de données pour jouer la migration, démarre le notebook pour regarder les données, lance l'API pour taper sur un endpoint. Il fait cela plusieurs fois par après-midi, sur plusieurs espaces de travail, et il ne nettoie pas, parce que rien ne le lui dit. Les processus survivent à la tâche. Le jeudi, l'interface porte une douzaine de services que vous n'avez pas lancés vous-même et que vous ne sauriez énumérer de mémoire, chacun se liant sans authentification parce que le guide de démarrage du framework dit que sur localhost tout va bien.
La position de Ray se lit différemment sur une machine qui fait tourner un agent de code : la sécurité et l'isolation doivent être imposées en dehors de la chose que vous avez lancée. Sur un portable où l'agent et le navigateur partagent un seul noyau, « en dehors » n'a nulle part où se poser.
Dans un espace de travail Bromure, 8265 n'est pas sur votre Mac
Bromure Agentic Coding fait tourner chaque espace de travail dans sa propre VM Ubuntu sur le framework Virtualization d'Apple. C'est toute la réponse à cette attaque, et elle tient sans que personne n'applique de correctif.
Quand l'agent lance ray start --head, le tableau de bord se lie à
127.0.0.1:8265 à l'intérieur de la VM. L'interface de loopback de votre Mac
est une autre interface. Un fetch() relié depuis Safari sur l'hôte résout
evil.example vers 127.0.0.1, se connecte au loopback de macOS, et n'obtient
rien, parce que le service que la page est venue chercher se trouve un noyau
plus loin. La chaîne publiée vise 127.0.0.1, l'adresse vers laquelle
singularity se relie et l'adresse que nomme l'avis de sécurité, et dans un
espace de travail Bromure il n'y a rien sur cette adresse.
Il en va de même pour la seconde moitié de l'avis, la partie sur l'usage d'un navigateur comme intermédiaire pour atteindre des instances Ray à l'intérieur d'un réseau d'entreprise que personne n'a mises sur internet. Les espaces de travail tournent en mode NAT par défaut, et le manuel est précis sur ce que cela apporte : les VM sont joignables depuis votre Mac mais ne sont pas exposées sur votre LAN physique, et les connexions entrantes venues d'ailleurs sont impossibles à moins de publier un service exprès. Publier, c'est un tunnel rapide Cloudflare par service que vous démarrez en appuyant sur un bouton.
Et vous obtenez l'inventaire. La carte Ports en écoute du tableau de bord de
l'espace de travail interroge ss -tulnpH dans l'invité et liste chaque socket
joignable de l'extérieur sous la forme IP-VM:port à laquelle vous vous
connecteriez, avec le nom du processus qui la détient. La carte masque les
sockets purement loopback, puisque rien à l'extérieur de la VM ne peut les
atteindre. La même liste sort de bromure-cli vm <id> -L. Cette liste est le
recensement des restes de l'agent, et macOS n'en tient aucun pour vous.
Le rebond d'après
ShadowRay 2.0 vaut la lecture pour ce que fait ensuite un nœud compromis, parce que c'est la partie dont l'isolation doit répondre. Il scanne à la recherche d'autres instances Ray et les infecte. Il installe une tâche cron qui va chercher de nouvelles instructions toutes les quinze minutes. Il tend la main vers les identifiants et les jeux de données présents sur la machine.
Chacun de ces gestes est une connexion sortante, et un espace de travail Bromure confronte les connexions sortantes à des règles que vous avez écrites. L'éditeur Connexions sortantes du volet Guardrails contient un jeu de règles de style pf : autoriser ou refuser, par hôte, IP ou CIDR, protocole et port, évalué de haut en bas avec la première correspondance qui gagne, plus un contrôle pour le trafic qui ne correspond à rien.
allow web api.github.com:443
allow tcp registry.npmjs.org:443
deny any 10.0.0.0/8
deny any 192.168.0.0/16
default deny
Deux couches appliquent ce même jeu de règles. Le commutateur virtuel évalue
chaque flux par IP de destination et par les noms d'hôtes qu'il a appris en
espionnant les réponses DNS de l'invité, si bien qu'une règle écrite contre un
nom continue de correspondre quand l'adresse bouge en dessous. Le proxy MITM
évalue les mêmes règles par SNI TLS, et pour les règles web par méthode HTTP,
ce qui vous permet d'écrire allow web api.example.com GET,HEAD. Bromure
injecte un reset sur une connexion TCP refusée, si bien que la connexion échoue
au lieu de rester suspendue.
Toute cette application tourne sur votre Mac, en dehors de la VM.
L'interception transparente est active par défaut et détourne le HTTP et le
HTTPS de l'invité vers Bromure même si quelque chose à l'intérieur efface
HTTP_PROXY et HTTPS_PROXY. Un agent qui a lu la mauvaise page web, ou une
charge arrivée avec un paquet, se trouve du mauvais côté des règles qu'il lui
faudrait modifier. Bromure écrit chaque décision dans Fenêtre → Chronologie de
sécurité sous forme de ligne Pare-feu : hôte, port, autorisé ou bloqué, dans
un unique tableau chronologique aux côtés des décisions sur les identifiants, la
chaîne d'approvisionnement et l'injection de prompt.
Le cron de quinze minutes a le même problème dans l'autre sens. Un espace de
travail, c'est trois couches de stockage, et deux d'entre elles sont à vous
pour être jetées : Effacer le home… ramène /home/ubuntu à son état
d'après-clonage, Réinitialiser à la base… re-clone le disque système de
l'espace de travail depuis l'image de base, et l'image de base reste en lecture
seule à l'exécution. Une crontab dans le répertoire home ne survit à aucune des
deux.
localhost était une convention
Les mainteneurs de Ray n'ont pas été négligents. Ils ont écrit un framework de calcul qui exécute des commandes, l'ont dit dans la documentation, et vous ont dit de mettre une frontière autour. Les gens qui le font tourner sur un portable ont pris l'interface de loopback pour cette frontière, ce qui est la conclusion à laquelle serveurs de développement, notebooks et tableaux de bord locaux invitent depuis vingt ans. C'était une convention plutôt qu'un contrôle. Elle tenait tant que les seules choses sur votre machine étaient des choses que vous aviez lancées vous-même, et elle a cessé de tenir dès que les navigateurs ont eu un moteur de script.
Un agent élargit l'écart chaque après-midi, un npm run dev à la fois. Le soin
ne le referme pas, parce que le soin exige de savoir ce qui écoute, et la liste
change à chaque commande que lance l'agent.
Cessez de les laisser partager une interface. Mettez le travail de l'agent dans
sa propre machine, où les ports qu'il ouvre répondent à votre Mac et à rien
d'autre, où les sockets joignables sont une liste que vous lisez plutôt qu'une
hypothèse dont vous avez hérité, et où les connexions qui sortent sont
confrontées à des règles que l'espace de travail ne peut pas modifier. Une page
qui se relie à 127.0.0.1 trouve alors une interface vide et encaisse un refus.
Sources : CISA, « CISA Adds One Known Exploited Vulnerability to Catalog » (17 août 2026) · The Hacker News, « CISA Flags Actively Exploited Ray Flaw That Can Trigger Browser-Based RCE » (18 août 2026) · Avis GitHub GHSA-q279-jhrf-cc6v (CVE-2025-62593) · Oligo Security, « ShadowRay 2.0 »