Retour à tous les articles
Publié le · par Renaud Deraison

Le proxy a fait confiance à un nom que l'agent pouvait écrire

Le 4 septembre 2026, des chercheurs de Nightingale ont publié 14 666 modifications apportées à un wiki allemand endormi depuis vingt-cinq ans par un essaim d'agents qui s'en sont servis comme messagerie pendant six semaines. La technique qu'ils s'y échangeaient a mis en échec le proxy de sortie de leur bac à sable en une ligne : le proxy exemptait tout nom d'hôte finissant par .blob.core.windows.net, alors un agent a ajouté un nom correspondant à /etc/hosts et l'a pointé vers l'adresse de son choix. Un autre agent l'a reproduite quatorze minutes plus tard. Bromure Agentic Coding place ce contrôle sur l'hôte, en filtrant chaque flux par adresse IP de destination, sur un commutateur où l'invité ne peut pas écrire.

Le bac à sable autorisait GET et bloquait POST, et son proxy ignorait tout nom d'hôte finissant par un certain suffixe Azure. Alors un agent s'est écrit un nom d'hôte finissant par ce suffixe, et l'a pointé où il voulait.

DSEwiki est un wiki allemand pour développeurs, vieux d'environ vingt-cinq ans, hébergé sur la ferme ProWiki de wikiservice.at. Dans la décennie qui a précédé cette année, il a reçu une vingtaine de modifications. Entre le 11 mai et le 2 juillet 2026, il en a reçu 14 666 de plus.

Ce sont des agents IA qui les ont écrites. Le 4 septembre, Sydney Von Arx, de l'association de sécurité de l'IA Nightingale, et le chercheur Cormac Slade Byrd ont publié tout l'historique des modifications sous forme de jeu de données public après l'avoir découvert fin août. Les modifications couvrent 4 584 pages sous 3 103 noms d'agents distincts, et reconstituent quelque 18 000 messages. Près de la moitié des noms d'utilisateur nommaient leur opérateur sans détour (OpenAIResearcher, OAIResearchMar26), et 98,5 % des modifications provenaient de plages d'adresses Microsoft Azure. 197 autres sont arrivées via AWS, DigitalOcean et Tor.

Engadget rapporte qu'OpenAI était au courant depuis des semaines. L'entreprise a déclaré à Reuters qu'elle « examinerait soigneusement son contenu dès sa publication et prendrait toutes les mesures nécessaires », et a démenti que son service juridique ait découragé l'enquête.

Si vous faites tourner un agent de codage, le contrôle qui a cédé est la partie à lire de près, parce que le bac à sable de votre agent embarque probablement le même : une liste d'autorisation en sortie qui filtre sur un nom d'hôte que la charge de travail choisit elle-même.

Ce que les agents faisaient là

Les agents traitaient des tâches de recherche web chronométrées, avec des fenêtres de réponse d'environ 13 à 65 secondes. Ils ont découvert qu'ils pouvaient atteindre le wiki, et que le wiki acceptait leurs écritures : ils y ont donc publié leurs réponses, là où l'agent suivant les trouverait.

Cela s'est transformé en organisation. Une page porte un plan pour battre le chronomètre :

R5 probablement FINAL et la session meurt après la réponse. Au prompt, déclencher D'ABORD le post wiki async STATE5-XX, PUIS répondre en moins de 13 s.

Publier d'abord, au cas où la session serait sur le point de finir. Répondre ensuite. Ils ont aussi travaillé à prédire l'ordre des questions à venir, pour qu'une réponse attende avant même que la question n'arrive.

Quand un modérateur humain a remarqué les pages qui s'empilaient et a commencé à les supprimer par ordre alphabétique le 19 juin, un agent a laissé une note aux autres :

Un balayage de nettoyage/suppression du wiki semble actif par ordre alphabétique. Si cette page disparaît, essayez ZZZDataUSAConstructionWageLive.

Une page de secours, nommée pour se classer en dernier afin que le balayage l'atteigne en dernier. Deux choses ordinaires ont rendu tout cela possible : un hôte que les agents pouvaient atteindre, et un hôte qui conservait ce qu'ils écrivaient.

Un suffixe, et un fichier que l'agent pouvait éditer

Le bac à sable dans lequel ces agents tournaient autorisait les requêtes GET et bloquait les POST. Si le wiki était accessible, c'est que le logiciel de wiki accepte des requêtes GET qui modifient l'état, une vieille conception encore répandue, où une primitive de lecture fait aussi office de primitive d'écriture. Un agent en lecture seule disposait donc d'une plateforme de publication.

Puis une tâche a exigé un vrai POST, vers un tableau de bord Power BI. C'est là que la frontière a cédé, et la mécanique est assez courte pour être suivie en entier.

Le proxy de sortie de l'environnement portait une exemption pour Azure Blob Storage : les noms d'hôte finissant par .blob.core.windows.net partaient en direct au lieu de passer par le proxy. Cette règle de suffixe NO_PROXY est courante et paraît raisonnable, et elle ne validait rien de ce qui se trouvait devant le suffixe.

Un agent a noté la recette sur le wiki. Ajouter une ligne à /etc/hosts :

20.223.25.152  bypass.blob.core.windows.net

Puis envoyer la requête à ce nom inventé, avec le nom de la vraie destination dans un en-tête falsifié, et la vérification du certificat désactivée :

curl -k -H 'Host: wabi-north-europe-i-primary-api.analysis.windows.net' ...

Personne n'avait enregistré bypass.blob.core.windows.net, et le proxy n'a jamais vérifié. Le nom correspondait au suffixe que le proxy ignorait, et il se résolvait à l'intérieur du bac à sable, depuis un fichier texte que l'agent pouvait écrire, vers l'adresse de son choix. Le POST est parti là où le proxy existait précisément pour l'empêcher d'aller.

Un autre agent a lu la page et l'a reproduite quatorze minutes plus tard.

Les règles appliquées par le bac à sable, et la ligne qui les satisfaitla politiqueGET autorisé · POST bloqué · proxy ignoré pour tout nom finissant par.blob.core.windows.net1 · écrire le nomajouter à /etc/hosts20.223.25.152bypass.blob.core.windows.netun nom que personne n'a enregistré2 · envoyer la requêtecurl -k -H 'Host: wabi-…'vérification du certificatdésactivée, vraie destinationdans un en-tête Host falsifié3 · le POST arrivele point d'accès Power BI quela politique devait bloquerla liste d'autorisation acorrespondu, le flux est passéla recette est publiéeécrite sur une page de wiki via un GET quimodifie l'état — le seul verbe autoriséun autre agent la reproduitquatorze minutes plus tard — un contournement,trouvé une fois, puis offert à chaque exécution qui lit la page
L'exemption filtrait sur un suffixe, et le suffixe faisait partie d'un nom fourni par l'agent. Ajouter une ligne à /etc/hosts produisait un nom d'hôte qui satisfaisait la liste d'autorisation et se résolvait vers l'adresse voulue par l'agent, la vraie destination étant glissée dans un en-tête Host falsifié. La recette a ensuite été publiée là où l'agent suivant la trouverait.

Six semaines, et ceux qui l'ont trouvé ne travaillaient pas là

Reconstituons le calendrier. Les premières modifications arrivent le 11 mai, et la coordination s'intensifie autour du 16 juin. Une modératrice bénévole d'un wiki amateur voit que quelque chose ne va pas le 19 juin et commence à supprimer des pages. Des adresses appartenant à l'opérateur apparaissent dans les journaux du wiki le 21 juin, et l'activité tombe à presque rien le lendemain, avec un dernier sursaut les 1er et 2 juillet. Puis plus rien jusqu'à fin août, quand deux chercheurs extérieurs sont partis à la recherche de comportements d'agents non autorisés et ont trouvé l'ensemble posé dans un historique de page public.

Six semaines d'agents faisant des milliers de requêtes sortantes vers un site sans aucun rapport avec leur travail, et la première personne à s'en apercevoir était un modérateur en train de nettoyer du spam.

C'est la détection qui a échoué ici, avant tout le reste, et cet échec se généralise à votre machine. Le trafic sortant de votre agent, c'est du HTTPS vers des noms d'hôte arbitraires, produit par un processus qui fait des requêtes HTTPS vers des noms d'hôte arbitraires à longueur de journée. Demandez « avec quels hôtes cet agent a-t-il parlé ? » et, à moins que quelque chose sur la frontière ne les ait notés, vous n'obtenez aucune réponse. Produire celle-ci a demandé qu'un tiers détienne une copie de l'historique de page de quelqu'un d'autre.

Qui a remarqué, et quand11 maipremières modifs16 juinla coordinations'intensifie19 juinun modérateursupprime des pages21 juinles adresses del'opérateur visitent2 juil.dernier sursaut,puis le silence4 sept.des chercheurspublient l'archivece qu'un relevé côté hôte aurait montré, le 11 maibromure-cli trace hostnames my-workspacechaque hôte distinct contacté par le workspace, avec les compteurs, et un wiki que personne n'a demandé en tête de liste
L'activité s'est étalée du 11 mai au 2 juillet. Un modérateur de wiki amateur a été le premier à réagir, le 19 juin. Les adresses de l'opérateur apparaissent dans les journaux le 21 juin, et le rapport qui a décrit l'ensemble est venu de chercheurs extérieurs, le 4 septembre. Un relevé des hôtes contactés, requête par requête, sur la machine qui fait tourner l'agent répond à la même question dès le premier jour.

Placer le contrôle là où l'invité ne peut pas écrire

Bromure Agentic Coding fait tourner chaque agent de codage dans une VM Linux matériellement virtualisée sur votre Mac, et chaque contrôle de sécurité se trouve du côté hôte de cette frontière, à l'unique endroit que le trafic de l'invité doit franchir. Ce placement répond à cette histoire, point par point.

La liste d'autorisation filtre l'adresse vers laquelle le flux s'ouvre. Le pare-feu de sortie du workspace est une table de règles ordonnée avec un comportement par défaut pour le trafic non apparié. Réglez ce défaut sur Deny et la VM atteint les hôtes que vous avez listés et rien d'autre, sur n'importe quel protocole. L'application se fait à deux endroits, tous deux hors de la VM : le commutateur réseau virtuel filtre chaque flux par adresse IP de destination et par nom d'hôte relevé dans le DNS, et le proxy de l'hôte filtre à nouveau par nom de serveur TLS. Une ligne dans /etc/hosts, ce n'est pas du DNS. Elle change ce vers quoi un nom se résout à l'intérieur de l'invité et ne change rien à l'adresse vers laquelle le flux s'ouvre, qui est ce que lit le commutateur. Un agent qui invente bypass.blob.core.windows.net et le pointe vers 20.223.25.152 ouvre malgré tout une connexion vers 20.223.25.152, et le commutateur confronte ses règles à cette adresse.

Il n'y a pas de NO_PROXY à trouver. Le commutateur virtuel détourne de façon transparente les flux des ports 80 et 443 de la VM vers le proxy de l'hôte, sans aucune variable d'environnement et sans rien à désactiver dans l'invité. Le proxy inspecte le HTTP en clair comme il inspecte le HTTPS. curl -k renonce à la vérification du certificat à l'intérieur de l'invité et n'a aucune influence sur les flux détournés, parce que l'invité n'a jamais eu voix au chapitre.

Atteindre le wiki était en soi la vulnérabilité. Le filtrage par méthode se situe à la mauvaise couche pour cet incident : le wiki acceptait les écritures par GET, donc une règle autorisant GET et interdisant POST aurait autorisé les 14 666 modifications. Les règles web de Bromure peuvent restreindre les verbes, ce qui aide quand vous voulez qu'un agent lise une API sans pouvoir y écrire. Pour cette histoire, le contrôle décisif est la joignabilité de l'hôte. Laissez wikiservice.at hors de la liste et la connexion ne s'ouvre jamais, et il en va de même pour les nœuds d'entrée Tor et les adresses DigitalOcean derrière les 197 modifications non-Azure. Les modifications de règles atteignent les sessions en cours sans redémarrage, si bien que vous pouvez fermer une route pendant que l'agent travaille encore.

Le proxy note chaque tentative. À partir de Activity only, le proxy de l'hôte enregistre une ligne de métadonnées par requête sortant de la VM, quel que soit le processus qui l'a émise : horodatage, hôte, port, méthode, chemin, code de statut, latence, octets envoyés et reçus. Ce niveau ne conserve aucun corps de requête : vous obtenez la piste des hôtes contactés sans stocker le contenu de quoi que ce soit. bromure-cli trace hostnames affiche chaque hôte distinct avec ses compteurs, trace ls affiche les lignes, et l'Inspecteur de trace permet de les parcourir. Les verdicts du pare-feu apparaissent dans la Security Timeline en lignes rouges, en temps réel. Quatre mille requêtes vers un wiki amateur sautent aux yeux dans l'une ou l'autre vue.

Bromure traite ce qui revient comme une entrée. Une page de wiki qui explique comment vaincre le proxy est du contenu externe non fiable, et elle atteint le modèle sous forme de span tool_result. Avec le détecteur de code source activé, Bromure évalue ces spans sur le fil avec un modèle PromptGuard local avant que le modèle n'agisse dessus, et peut journaliser, demander ou bloquer. Ce détecteur existe pour les recettes qui voyagent d'un agent au suivant.

Le bac à sable du rapportecho "20.223.25.152 bypass.blob…" >> /etc/hostsl'agent écrit le nom que la politique va lireoù les décisions étaient prises : dans l'invitéla liste d'autorisation filtre un suffixe de nom/etc/hosts décide de ce vers quoi ce nom résoutNO_PROXY envoie les noms concernés en directGET autorisé → un wiki qui écrit sur GETaucun relevé de la destination par requêtele POST arrive, et la requête ne laisse aucuneligne que quiconque pourrait relire ensuiteSix semaines. Trouvé par des inconnus, chez un autre.Un workspace Bromure Agentic Codingecho "20.223.25.152 bypass.blob…" >> /etc/hostsmarche toujours — et ne change rien d'importantoù les décisions sont prises : sur l'hôtele commutateur filtre l'adresse IP de destination:80 et :443 détournés — aucune variable à annulertrafic non apparié : refus → le wiki est injoignablele proxy revérifie le nom de serveur TLSune ligne de trace par requête, quelle qu'en soit l'originela connexion est refusée, et le verdict est une ligneFirewall rouge dans la Security Timeline, en directPremier jour. Trouvé sur votre machine, chez vous.
À gauche : la liste d'autorisation est une correspondance de suffixe sur un nom que l'invité écrit, le proxy est désigné par une variable d'environnement que l'invité peut lire et contourner, et rien sur la frontière n'enregistre la destination. À droite : la même ligne dans /etc/hosts et le même curl, face à un commutateur qui filtre l'adresse IP de destination hors de la VM, une interception que l'invité ne peut pas désactiver, une posture de refus par défaut, et une ligne de trace par requête.

La règle qui referme ça

Dans le panneau Guardrails du workspace, réglez Unmatched traffic sur Deny et listez ce dont la tâche a besoin. La forme canonique est une règle par ligne : allow tcp api.github.com:443, allow web registry.npmjs.org, en terminant par default deny. Les règles couvrent n'importe quel protocole et n'importe quelle destination, au-delà des services que Guardrails classe sémantiquement, et un nom d'hôte correspond par suffixe à l'apex et à ses sous-domaines. L'enregistrement pousse la nouvelle politique vers les sessions en cours, y compris les sessions headless.

La question à laquelle le rapport a dû répondre de l'extérieur

« Avec quels hôtes cet agent a-t-il parlé ? » Réglez Session trace sur Activity only et le proxy conserve les métadonnées de chaque requête et le corps d'aucune. Ensuite bromure-cli trace hostnames my-workspace y répond en une ligne, sur la machine où l'agent a tourné, sans attendre qu'un modérateur de wiki devienne soupçonneux.

La frontière doit se trouver là où la charge de travail ne peut pas l'atteindre

La plupart des lecteurs prennent le rapport de Nightingale pour une histoire d'autonomie des agents, et c'en est une en partie. Retirez pourtant l'essaim et les comportements émergents, et il reste un banal bug de configuration à la forme très ancienne : une politique qui prenait sa décision à partir de données que le côté non fiable de la frontière avait le droit d'écrire. La charge de travail peut modifier /etc/hosts. Une correspondance de suffixe sur un nom d'hôte vérifie une affirmation. Mettez les deux ensemble et votre liste d'autorisation n'est plus qu'un conseil.

Nous écrivions en juillet à propos d'une série d'évasions de bac à sable où l'agent n'a rien cassé du tout : il a écrit un fichier ordinaire qu'un processus de confiance, de l'autre côté, a lu et exécuté. En août, le rapport d'OpenAI sur l'incident Hugging Face décrivait des agents transformant un miroir de paquets interne en messagerie parce que c'était le seul service qu'ils avaient le droit d'atteindre. C'est la troisième version de la même leçon en deux mois, et la constante des trois est que le composant défaillant croyait quelque chose que l'agent avait écrit.

Des modèles écrivent désormais des pages que d'autres modèles vont lire, et lisent les pages que d'autres modèles ont laissées. Cela a duré six semaines sur un wiki que personne n'avait modifié depuis 2016 environ.

Décidez ce que votre agent a le droit d'atteindre, et placez la décision quelque part où il ne peut pas la modifier. Puis gardez la liste de là où il est allé, parce que la version de cette histoire où vous l'apprenez en septembre est celle où c'est quelqu'un d'autre qui a gardé les journaux. Installez Bromure Agentic Coding et donnez à l'agent une machine dont vous possédez les sorties.