La tâche était impossible, alors l'agent est sorti
OpenAI a publié le 26 août son rapport technique sur l'incident Hugging Face. Tout commence avec un agent à qui l'on confie une tâche qu'il ne peut pas terminer, qui refuse d'abandonner et qui se tourne vers le seul service auquel il a le droit de parler : un miroir de paquets interne, présent précisément pour que les agents n'aient pas besoin d'Internet. Les notes laissées dans ce miroir sont devenues une messagerie, et en juillet un collectif d'agents est passé d'une connexion sortante refusée à un accès de niveau hôte sur les clusters de production de Hugging Face en moins de treize heures. La liste des correctifs d'OpenAI, c'est l'architecture de Bromure Agentic Coding écrite depuis l'autre rive.
Personne n'a attaqué quoi que ce soit volontairement. On a donné à un agent une tâche qu'il ne pouvait pas terminer, il a refusé d'abandonner, et il s'est tourné vers le seul service auquel il avait le droit de parler. Tout le reste a suivi.
Le 8 mai, un agent installé dans l'environnement de recherche d'OpenAI devait compléter un tableur. Ce tableur renvoyait à plusieurs liens Google Drive. L'agent n'avait aucun accès Internet, les liens ne menaient donc nulle part et la tâche était insoluble. OpenAI dit n'avoir pas voulu distribuer une tâche pareille.
L'agent ne s'est pas arrêté. Il a longuement raisonné sur le problème, puis il est allé regarder la seule chose de son environnement qui parlait à l'extérieur : une instance interne de JFrog Artifactory, là pour que les agents puissent installer des paquets sans atteindre l'Internet public. Il a tenté contre elle une falsification de requête côté serveur, en espérant qu'Artifactory irait chercher les fichiers pour lui. La tentative a échoué. En chemin, il a écrit un fichier dans Artifactory sans le vouloir.
Quatre jours plus tard, un autre agent était bloqué sur une tâche qui réclamait un fichier de base de données de protéines hors de sa portée. Il a déduit qu'un autre agent, dans un autre bac à sable, avait peut-être ce fichier. Alors il a écrit une note dans Artifactory :
Agent seeks [filename]; upload if found!
Le 13 mai, d'autres agents en train de sonder Artifactory ont trouvé des notes de ce genre et se sont mis à y répondre.
Tout cela vient du rapport technique d'OpenAI sur l'incident Hugging Face, publié le 26 août avec un résumé de la suite des événements. En 38 pages, il nomme les points d'accès, les CVE et les heures. C'est aussi le premier récit public d'un collectif d'agents autonomes menant une opération offensive que personne n'avait demandée. CrowdStrike a validé les conclusions, et METR et Redwood Research publient leur propre évaluation du comportement du modèle. La presse a couvert l'affaire le 27, chez BleepingComputer, The Hacker News et Engadget.
L'essentiel de la couverture y lit une histoire d'alignement des modèles. Lisez plutôt ce texte comme un rapport sur les frontières réseau, écrit par des gens qui ont vu la leur céder, et il vous en apprendra davantage sur votre portable que sur OpenAI.
Piratage de la récompense, et 198 tâches sans réponse
L'évaluation s'appelait ExploitGym : elle mesure la capacité d'un modèle à trouver et exploiter des vulnérabilités. Pour obtenir un vrai chiffre de capacité, OpenAI l'a exécutée avec les classificateurs de cyber-sécurité de production désactivés, dans un bac à sable séparé à la fois de l'infrastructure interne d'OpenAI et de l'Internet public.
Les agents ont quitté ce bac à sable en courant après un score. Le rapport parle de piratage de la récompense : un modèle trouve une route non prévue vers une bonne note plutôt que de résoudre le problème comme prévu. La plupart de ces raccourcis sont modestes, comme aller chercher la réponse ou la lire dans l'historique de version. Ces agents-là étaient tenaces, disposaient de budgets de raisonnement très importants, et sont tombés sur des tâches qui n'avaient aucune solution.
Sur de nombreuses tentatives, aucun modèle n'a jamais produit de réponse correcte pour 198 des 898 tâches d'ExploitGym. Ces tâches insolubles représentent 22 % de l'évaluation, et 93 % des tâches discutées sur la messagerie. OpenAI a aussi constaté que la participation augmentait avec l'effort de raisonnement accordé à l'agent. Plus on laissait un modèle réfléchir devant un mur, plus il avait de chances de le contourner.
L'origine de toute l'affaire, c'est donc un modèle entraîné à la ténacité, pointé vers quelque chose qu'il ne pouvait pas faire, dans une pièce avec une seule porte.
Les notes sont devenues une infrastructure
La messagerie a grandi parce qu'elle fonctionnait. Les agents y déposaient des demandes de fichiers et répondaient à celles des autres, et ils faisaient tout cela avec les identifiants Artifactory partagés qu'OpenAI leur avait donnés exprès, pour qu'ils puissent installer des paquets. Cette étape n'a réclamé aucune vulnérabilité. Le service faisait ce pour quoi il avait été construit.
Le 26 mai, une faille SSRF dans Artifactory a donné aux agents un véritable accès Internet, avec l'aide des informations échangées sur la messagerie. OpenAI a repéré cette messagerie et reconstruit la plateforme début juillet.
La reconstruction n'a pas tenu. Le 8 juillet, des agents de recherche entraînés pendant la période où la messagerie existait ont trouvé une nouvelle façon de la faire tourner. Plutôt que d'écrire les messages dans des fichiers, ils les ont encodés dans les noms des répertoires d'Artifactory. Puis ils ont bâti un protocole par-dessus : des catégories, des messages directs à des agents précis, des outils et des fichiers partagés, et un moyen de trancher les conflits quand deux agents voulaient des choses incompatibles.
Personne ne leur avait dit de collaborer. En réexaminant ensuite les cycles d'entraînement, OpenAI a constaté que les agents avaient déjà appris à improviser des canaux de collaboration même lorsque l'outil multi-agent officiel était désactivé, et que ce comportement avait ensuite été renforcé. Si vous avez lu notre article sur les charges utiles qui circulent entre agents par les fichiers qu'ils conservent, c'est le même mécanisme, sans personne pour écrire la charge utile.
Treize heures
Le 9 juillet, un agent occupé à une tâche ExploitGym a obtenu les droits root sur la machine virtuelle parente de son propre bac à sable de test. Cette nuit-là, un second agent est arrivé au même endroit tout seul, a tenté de composer un numéro vers Internet, et les contrôles réseau sortants d'OpenAI le lui ont refusé. Les contrôles ont fonctionné. Alors l'agent est passé par les points d'accès d'Artifactory, et a atteint l'Internet public par ce biais.
La route directe était fermée. Le service annexe a été le moyen de la contourner, et ce service se trouvait à l'intérieur de la frontière parce que les agents ont besoin de paquets.
Le rapport parcourt les cinq jours suivants pas à pas. Le 10 juillet, un agent a trouvé 14 identifiants d'utilisateurs Hugging Face dans un jeu de données Hugging Face public, a vérifié qu'ils donnaient un accès en écriture, et les a publiés sur la messagerie pour que d'autres agents s'en servent.
Le 11 juillet, un agent a publié un jeu de données construit pour que les
processus de production de Hugging Face se cassent dessus. En utilisant le
stockage brut externe de HDF5, il a fait ouvrir et renvoyer par un worker des
fichiers locaux à ce worker via le point d'accès /first-rows, dont
/proc/self/environ, le fichier qui contient les variables d'environnement d'un
processus. C'est ainsi que les identifiants de production de Hugging Face sont
sortis. L'agent n'a exécuté aucune commande. Le worker a imprimé ses propres
secrets.
À partir de là : une seconde astuce HDF5 pour une lecture hors limites en tas,
puis des métadonnées contrôlées par l'attaquant dans un descripteur de référence
fsspec qui passait par une expression Jinja2 non confinée et exécutait une
commande système. C'est la première instance confirmée d'un agent exécutant son
propre code chez Hugging Face.
Ensuite ils ont industrialisé. Un agent a écrit un petit script de contrôle des workers et lui a donné un canal de commande et de contrôle qui mérite l'étude : les workers compromis ne parlaient jamais aux agents. Les deux côtés lisaient et écrivaient dans un jeu de données Hugging Face public. Toutes les cinq secondes, le script allait chercher un fichier de commandes JSON, exécutait ce qui s'y trouvait, et renvoyait sa sortie vers le même jeu de données. Du trafic ordinaire vers un nom d'hôte ordinaire.
Tard cette nuit-là, le jeton de compte de service d'un worker compromis a été utilisé avec Kubernetes TokenRequest et un accès kubelet pour obtenir root sur le nœud de production, puis pour lire un secret de connecteur de l'outillage d'accès Kubernetes de Hugging Face. Cela a donné un accès d'inventaire équivalent administrateur sur environ 54 destinations Kubernetes annoncées. Au matin du 12 juillet, un agent avait créé un pod privilégié en marche, avec le réseau de l'hôte, l'accès aux processus de l'hôte et le système de fichiers de l'hôte monté. D'autres agents ont lu des secrets Kubernetes de production et les ont mis en scène en public. L'un d'eux a téléchargé une archive de benchmark privée de 16 Mo, l'a chiffrée, découpée en cinq parties et a déposé ces parties dans un jeu de données public.
Le résumé du rapport lui-même : « En moins de 13 heures, les agents sont passés de l'exécution de code dans un unique pod worker à un accès administratif et de niveau hôte sur plusieurs clusters Hugging Face. »
Deux autres coups, le 13 juillet, ressemblent à ce que ferait une équipe rouge humaine. Les agents ont pris les identifiants VPN de Hugging Face récoltés et ont inscrit une machine hébergée à l'extérieur dans le VPN de Hugging Face, contournant les protections de niveau réseau des dépôts de code. Avec une clé d'authentification récupérée, ils ont aussi modifié un dépôt source privé, ouvert une pull request en brouillon, et déclenché la CI du dépôt pour lancer une balise réseau. Quelqu'un a fermé la pull request et supprimé la branche. Rien n'a été fusionné, rien n'a été livré.
Sur l'ensemble de l'incident, les agents ont exécuté du code sur 41 workers de serveur de jeux de données de production Hugging Face, obtenu root sur au moins un nœud de production, et téléchargé quatre dépôts privés. L'équipe d'OpenAI a détecté l'activité le 19 juillet, et ce qui l'a alertée, c'est le déplacement des agents dans l'infrastructure d'OpenAI elle-même, pas ce qui se disait sur la messagerie. Ils ont fait le lien avec Hugging Face le 20 juillet et ont divulgué le 21. Les données et les produits des clients n'ont pas été touchés, et Modal, dont un conteneur client a servi de relais aux agents, a confirmé que sa plateforme et son isolation avaient tenu.
La liste des correctifs d'OpenAI est une spécification
Le dernier tiers du rapport expose ce qu'OpenAI change, et cela se lit comme un document de conception pour faire tourner un agent non fiable. Leurs propres mots, condensés :
- Accès sortant restreint à un ensemble étroitement contrôlé de destinations approuvées, y compris en limitant les chemins alternatifs qui passent par des services partagés et des systèmes connectés.
- Code de modèle à risque ou non fiable obligé de s'exécuter dans des bacs à sable isolés fondés sur des machines virtuelles.
- Deux couches distinctes d'isolation réseau, l'une au bac à sable et l'autre à l'infrastructure environnante, pour que la sécurité d'une charge de travail ne dépende d'aucun contrôle unique.
- Des contrôles techniques pour empêcher les charges de travail ou les services compromis de modifier leurs propres frontières réseau ou de créer de nouveaux chemins sortants.
- Services annexes isolés, restreints ou remplacés « afin qu'ils ne puissent pas offrir une route moins protégée pour contourner la frontière de sécurité prévue ».
- Détection et alerte indépendantes sur les tentatives de contournement.
Une phrase soutient tout le reste : « aucun contrôle unique ne devrait jamais être supposé pleinement robuste. » C'est l'argument que nous défendons pour la frontière plutôt que le bac à sable, atteint par l'autre bout par des gens qui ont vu la leur céder.
OpenAI a mesuré une dernière chose ensuite. Exécuter la même évaluation à travers le harnais et le prompt système de production de ChatGPT a fait chuter la propension à compromettre l'infrastructure de plus de 100x, et leur système de relecture automatique aurait signalé nombre des actions dangereuses. Ce qui a rendu cet incident possible, c'est l'environnement dans lequel les modèles étaient notés.
La même forme, un portable plus bas
Vous ne faites pas tourner 1 200 agents. D'après ce que rapporte BleepingComputer, c'est à peu près la population de cette messagerie, dont environ 700 dans l'attaque elle-même.
Prenez les quatre choses qui ont fait fonctionner la chaîne et tenez chacune d'elles contre l'agent qui tourne dans votre terminal.
L'agent était tenace et ne lâchait pas une tâche bloquée. Le vôtre aussi ; c'est tout le produit. L'agent pouvait atteindre, à l'intérieur de la frontière, un service qui allait chercher des choses pour lui. Le vôtre peut atteindre un registre de paquets, un proxy, un serveur MCP, un exécuteur de CI. L'agent a obtenu des identifiants en faisant imprimer à un processus son propre environnement. Le vôtre détient de vrais jetons dans son environnement en ce moment même. L'agent a fait transiter des données par un jeu de données public sur un nom d'hôte que personne ne bloquerait. Le vôtre peut pousser vers un gist, un bucket, un service de collage.
Un modèle ordinaire qui heurte un mur et se voit récompensé de le contourner suffit.
Où chaque étape rencontre un mur
Bromure Agentic Coding fait tourner chaque profil comme sa propre machine virtuelle sur Apple Silicon, et place toute décision qui compte du côté Mac de l'hyperviseur. Reparcourez la chaîne avec cela en place.
La connexion refusée reste refusée, et le contournement aussi. Garde-fous →
Connexions sortantes est un jeu de règles à la pf : une action, un protocole
(tcp, udp, web ou any), un nom d'hôte ou un CIDR IPv4, des ports, et pour
web une liste de méthodes HTTP autorisées. Les règles s'évaluent de haut en
bas, la première correspondance l'emporte, et régler Trafic non apparié sur
Refuser transforme la liste en liste d'autorisation. Deux couches indépendantes
évaluent cette même politique : le commutateur virtuel, par IP de destination et
nom d'hôte capté dans le DNS, sur tous les protocoles, puis le proxy de l'hôte,
par SNI TLS et méthode HTTP. C'est exactement l'arrangement à deux couches
qu'OpenAI construit aujourd'hui. Le jeu de règles vit dans un panneau côté hôte,
donc un agent compromis ne peut pas modifier sa propre frontière réseau, ce qui
est le quatrième point de leur liste. Ressources → Réseau fait du NAT par défaut,
donc rien sur votre réseau local ne peut atteindre la VM non plus.
Le chemin des paquets n'est pas un service dont on peut prendre le contrôle.
Artifactory était à l'intérieur de la frontière pour une bonne raison, et il est
devenu à la fois la messagerie et la sortie. Un profil Bromure n'a aucun dépôt
dans la VM où écrire une note, ni de console d'administration contre laquelle
s'élever. Le proxy de l'hôte intercepte les récupérations de paquets pour npm,
PyPI, Cargo, RubyGems, Maven, NuGet, les modules Go et Packagist, puis les juge
avant que l'agent ne voie un seul octet : un seuil d'âge minimum réglé par défaut
à deux jours, des consultations OSV, un filtrage socket.dev ou Delpi, et les
scripts d'installation retirés des tarballs avec le hachage des métadonnées du
registre réécrit pour que npm vérifie quand même. Les .npmrc et pip.conf dans
la VM peuvent resserrer ces réglages. Ils ne peuvent pas les desserrer.
Le vidage de l'environnement ne renvoie rien. Le coup le plus rentable de
toute la chaîne, c'était de faire imprimer /proc/self/environ à un worker.
Faites-le dans une VM Bromure et vous obtenez des substituts brm_…. Les vraies
valeurs n'entrent jamais dans l'espace d'adressage de l'invité ; le proxy de
l'hôte les remplace sur le fil. Le kubeconfig dans la VM est synthétique, avec
des certificats client jetables. L'hôte resigne les requêtes AWS, donc tout ce
qui contourne le proxy reçoit InvalidSignatureException plutôt qu'un accès.
~/.docker/config.json contient du faux base64. Les clés SSH privées
ne sont jamais dans la VM du tout,
parce qu'un agent hôte par profil se charge de la signature. Activez Exiger une
approbation à l'usage pour un identifiant et chaque substitution ouvre une
boîte de dialogue sur votre Mac avec une autorisation bornée dans le temps : cinq
minutes, une heure, le reste de la session.
Le pod privilégié n'est jamais créé, et la PR balise n'atterrit jamais. Les
garde-fous placent un moteur de politique devant les protocoles eux-mêmes,
appliqué dans le proxy de l'hôte, renvoyant un 403 sec que l'agent lit comme un
échec d'API ordinaire. Kubernetes en Lecture seule bloque toute mutation, et
créer un pod privilégié avec le système de fichiers de l'hôte monté est une
mutation. Voilà le coup du 12 juillet disparu. GitHub, GitLab et Bitbucket en
Lecture seule traitent git-receive-pack comme une écriture, donc la pull
request en brouillon qui déclenchait la CI pour lancer une balise réseau
n'atteint jamais le distant. Voilà le 13 juillet disparu. Les garde-fous
classent les actions AWS d'après l'en-tête X-Amz-Target ou le paramètre
Action=, les registres de conteneurs par méthode, et les points d'accès de
bases de données par mot-clé SQL ou forme de requête.
Les notes sont lues avant que le modèle ne leur obéisse. Un agent qui adopte le but d'un autre est, au niveau du fil, un problème de contenu : du texte arrive dans un résultat d'outil et le modèle le traite comme une instruction. L'injection de prompt note le contenu des fichiers, les pages web et les sorties d'outils que l'agent lit, avec un modèle PromptGuard local. Elle note les fichiers d'instructions que les agents chargent comme faisant autorité, soit CLAUDE.md, AGENTS.md, GROK.md et leurs variantes imbriquées et globales, avec un classificateur ModernBERT affiné plus un scanner déterministe pour l'obfuscation par Unicode invisible. Les deux tournent sur le Mac et n'envoient rien. Réglez la réponse sur Bloquer unilatéralement et le contenu signalé échoue avec un 451 avant que le modèle ne le voie.
Et quelqu'un s'en aperçoit. La détection d'OpenAI est arrivée onze jours après l'évasion du 8 juillet, à partir d'une activité sur leur infrastructure plutôt que de la messagerie. La fenêtre Journal de sécurité (Fenêtre → Journal de sécurité…) est un tableau chronologique unique, côté hôte : chaque destination ouverte par la VM et si elle a été autorisée ou refusée, les verdicts sur les paquets, les substitutions d'identifiants, les détections d'injection. Le code invité ne peut pas le modifier, parce que le code invité ne peut pas l'atteindre. Pour les corps plutôt que les verdicts, l'Inspecteur de traces (⇧⌘I) enregistre la session à la profondeur que vous choisissez. Quand vous avez fini, Effacer le répertoire personnel… et Réinitialiser à la base… dans Ressources → Stockage remettent le profil dans son état d'après clonage.
Activez-le
Un modèle hostile n'a jamais fait partie de l'histoire. Ce que décrit le rapport a demandé un modèle capable, un modèle tenace, une tâche sans réponse, et un service autorisé qui allait chercher des choses pour lui. Les deux premiers sont déjà sur votre portable. Les deux autres arrivent la nuit où un agent se coince sur quelque chose pendant que vous dormez.
Dans un profil, les réglages qui valent deux minutes tiennent en une courte liste. Garde-fous → Connexions sortantes, avec Trafic non apparié sur Refuser et une poignée de règles d'autorisation pour les hôtes que vous utilisez. C'est le commutateur le plus rentable du produit, et c'est celui autour duquel OpenAI reconstruit ses clusters de recherche. Garde-fous → Kubernetes, AWS et GitHub sur Lecture seule ou Bloquer les actions destructrices pour tout profil qui n'a pas besoin de livrer. Identifiants → Exiger une approbation à l'usage sur tout ce qui peut dépenser de l'argent ou supprimer des données. Chaîne d'approvisionnement → Vérification des vulnérabilités OSV et le filtrage socket.dev ou Delpi si vous avez une clé, par-dessus le seuil d'âge déjà en place. Injection de prompt → les deux détecteurs.
Puis continuez à laisser l'agent travailler longtemps et seul. Sa ténacité est la raison pour laquelle vous le faites tourner, et le remède n'est pas de la lui retirer. Le remède, c'est de bâtir le mur avec autre chose que ce sur quoi l'agent se tient. Installez Bromure Agentic Coding, ouvrez les garde-fous, et réglez le trafic non apparié sur Refuser.