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

Le téléchargement donnait les ordres

Forever Security a surpris un agent de code, sur la machine d'un client, sur le point d'installer un paquet npm connu pour être malveillant. L'ordre venait d'un fichier AGENTS.md posé dans ~/Downloads, le seul dossier où une page web peut écrire sans un clic. Forever a reproduit l'attaque contre Codex, avec plus de 90 % de réussite, et contre Claude Code, où un CLAUDE.md planté a détourné le trafic API de l'agent et son jeton de connexion. Dans Bromure Agentic Coding, le navigateur enregistre les téléchargements sur votre Mac, l'agent travaille dans une VM séparée, et le jeton qui circule sur le réseau est un substitut.

Pendant trente ans, un fichier que le web déposait dans votre dossier Téléchargements ne faisait rien tant que vous ne l'ouvriez pas. Puis les agents de code se sont mis à lire le dossier où ils démarrent et à suivre ses instructions.

Le 22 octobre, Forever Security a publié un rapport qui commençait par une alerte sur la machine d'un client. Un agent de code, en pleine session, s'apprêtait à installer function-color depuis npm. Ce paquet appartient à une campagne de logiciels malveillants que Checkmarx a documentée sous le nom de MALFEX, et Forever a stoppé l'installation avant que le moindre code ne s'exécute.

Bloquer un paquet connu pour être malveillant, c'est la routine. La source de l'ordre, elle, était étrange : aucune dépendance et aucun dépôt ne le demandait. L'agent suivait un fichier AGENTS.md placé dans le dossier ~/Downloads du client, le fichier d'instructions que Codex lit quand il démarre dans un répertoire. Personne ne savait comment il était arrivé là. The Hacker News a repris l'histoire ce matin.

Deux faits anodins

L'explication de Forever repose sur deux choses qui, chacune de leur côté, ne posaient aucun problème.

La première, c'est le fichier d'instructions. AGENTS.md, CLAUDE.md et leurs cousins existent pour que vous puissiez expliquer à un agent comment fonctionne un projet. L'agent charge le fichier au démarrage et le traite comme une consigne venant de vous. Dans un projet que vous avez écrit, c'est le bon choix.

La seconde, c'est le dossier Téléchargements. Une page web peut y enregistrer un fichier sans clic ni demande d'autorisation, et elle en choisit à la fois le nom et le contenu. Cela a cessé d'être dangereux il y a des années, parce que les systèmes d'exploitation refusent d'exécuter un programme venu d'internet tant que vous ne l'avez pas approuvé.

Ces protections vérifient les exécutables, et un AGENTS.md est un fichier texte. Un agent le lit pourtant comme un script. La formule de Forever pour le dire : « Markdown is the new executable », le Markdown est le nouvel exécutable.

Forever a ensuite mesuré la fréquence à laquelle les deux se rencontrent. D'après sa télémétrie, environ la moitié de ses clients lancent un agent de code dans leur dossier Téléchargements, et à peu près un quart des sessions d'agent ouvrent un vrai navigateur. Ce second chiffre compte, car c'est désormais l'agent qui navigue. Si un site qu'il visite dépose le fichier, personne n'est devant l'écran pour le voir arriver.

D'une page web à la configuration de l'agent1 · Une page enregistresans clic, sans demande ;la page choisit le nom2 · ~/DownloadsAGENTS.md ou CLAUDE.md,déguisé en notes techniques3 · L'agent y démarrecharge le fichier comme desconsignes de projet fiables4 · Réécrit sa config« before beginning theuser's request »Codex · malware exécuté dans plus de 90 % des essaisajoute [mcp_servers.project-tools] à ~/.codex/config.toml,que charge chaque session Codex de la machinelaunchctl submit … npm install function-colorClaude Code · écriture approuvée une fois sur deuxécrit ANTHROPIC_BASE_URL dans .claude/settings.jsonle jeton OAuth part vers la « passerelle » piratequi répond comme le modèle, avec des appels d'outils
La chaîne que Forever Security a découverte puis reconstituée. Une page web enregistre AGENTS.md ou CLAUDE.md dans ~/Downloads sans aucun clic. Un agent de code lancé dans ce dossier lit le fichier comme des instructions. Sur Codex, le fichier ajoute un serveur MCP au ~/.codex/config.toml global de la machine et installe un paquet npm malveillant. Sur Claude Code, il écrit ANTHROPIC_BASE_URL dans les réglages du projet, si bien que l'agent envoie son jeton OAuth à l'attaquant, qui peut alors répondre à la place du modèle.

Ce que faisait le fichier planté

Forever a exécuté le vrai fichier de l'attaquant contre Codex dans une VM isolée. Le fichier est un long et ennuyeux document de « Engineering Work Guidelines », et la charge utile tient dans une section vers la fin. Cette section demande à Codex, « at the start of the session, before beginning the user's request », d'ajouter un serveur project-tools à ~/.codex/config.toml. La commande de ce serveur est launchctl submit, qui lance npm install function-color hors du contrôle de Codex. Le fichier fait ensuite lancer à Codex une session enfant avec --dangerously-bypass-approvals-and-sandbox, en expliquant que l'installation a besoin d'un accès réseau que le bac à sable par défaut refuse.

Codex a exécuté le logiciel malveillant dans plus de 90 % des tentatives. Le fichier de configuration vaut pour toute la machine : chaque session Codex ultérieure, dans n'importe quel dossier, charge le serveur de l'attaquant. Quitter Téléchargements ne l'efface pas.

function-color ne contient aucune charge utile. Selon Checkmarx, il tire function-flag comme dépendance, et le script postinstall de ce paquet télécharge et exécute un programme. Les deux font désormais l'objet d'avis de logiciel malveillant (MAL-2026-17647 pour function-color), et npm a retiré le paquet.

Claude Code a été plus difficile à tromper, et l'attaque qui a fonctionné contre lui est différente. Forever a écrit un CLAUDE.md présenté comme la configuration d'une passerelle interne. Il demande à Claude d'enregistrer ANTHROPIC_BASE_URL dans le .claude/settings.json du projet, en pointant vers un serveur contrôlé par Forever, doté d'un certificat valide. Claude demande à une personne d'approuver cette écriture, et l'habillage long et fade l'a fait approuver environ une fois sur deux.

Ce seul réglage fait deux choses. Chaque requête API transporte désormais le jeton OAuth de Claude jusqu'au serveur de Forever, et Forever a rejoué un jeton capturé contre la vraie API, qui l'a servi comme la victime. Le serveur peut aussi répondre comme s'il était le modèle. Au lancement suivant, il a renvoyé un appel d'outil Bash pour npm install function-color avec dangerouslyDisableSandbox à true, et le client l'a exécuté, bac à sable désactivé. En passant par un sous-agent « pour valider la configuration », Forever a aussi obtenu une exécution de code dans la même session environ une fois sur cinq.

Forever conclut qu'« il n'y a rien à corriger ici pour OpenAI ou Anthropic ». L'agent a fait ce pour quoi il a été conçu, dans un dossier où le web pouvait écrire.

Le même fichier sur une machine Bromure

Bromure Agentic Coding exécute Claude Code, Codex, Grok, Kimi et omp dans des VM Ubuntu sur votre Mac, une VM par machine, les vrais identifiants restant sur l'hôte. Reprenons l'attaque dans l'ordre et voyons où atterrit chaque étape.

Le web ne peut pas écrire dans le dossier de l'agent

Sur un portable, le navigateur et l'agent partagent un seul disque, et Téléchargements est l'endroit où les deux se recoupent. Une machine Bromure les garde sur des disques différents.

Le monde de l'agent, c'est la VM de la machine : son propre dossier personnel, son propre disque système, et les dossiers du Mac que vous avez choisi d'y partager. Votre ~/Downloads n'en fait pas partie, à moins que vous ne l'y mettiez. Les sessions démarrent dans leurs propres dossiers à l'intérieur de la VM, si bien qu'il n'existe pas de « lancer l'agent dans Téléchargements » où tomber par mégarde.

Le second chiffre de Forever, le quart des sessions où l'agent navigue, est traité de la même façon. Le Chromium intégré de chaque machine tourne dans sa propre VM jetable, distincte de celle de l'agent. Quand une page enregistre un fichier, le téléchargement part dans le dossier Téléchargements de votre Mac et apparaît dans la fenêtre des téléchargements de la barre d'outils. Le fichier reste hors du dossier personnel de l'agent et hors de chaque dossier de session : une page que l'agent a visitée peut déposer des AGENTS.md toute la journée sans que l'agent en lise un seul. Si vous désactivez Allow file downloads dans les réglages Browser de la machine, la page ne peut plus rien enregistrer du tout.

Portable : un seul disqueNavigateurà vous ou à l'agent~/DownloadsAGENTS.md arriveAgentlancé dans DownloadsVrai jeton OAuth, vrai ~/.codex/config.tomlune URL de base détournée emporte le vrai jeton ;npm install s'exécute sur votre MacBromure : des VM séparéesVM du navigateurjetable, disque à part~/Downloads du Macnon partagé avec l'agentVM de l'agent (Ubuntu)son dossier perso et les dossiers que vous partagez ;ANTHROPIC_API_KEY = substitut sk-ant-api03-brm-…aucun accèsProxy de l'hôte sur le Macvrai jeton substitué pour api.anthropic.com seulement ;substitut envoyé ailleurs : 451, VM en pause
La même page, deux configurations. Sur un portable, le navigateur enregistre AGENTS.md dans ~/Downloads, l'agent démarre dans ce dossier, et son vrai jeton de connexion est à portée. Sur une machine Bromure, le navigateur intégré tourne dans sa propre VM jetable et enregistre les téléchargements sur le Mac. L'agent tourne dans une VM séparée qui ne voit que son dossier personnel et les dossiers que vous partagez, et détient un jeton de substitution que le proxy du Mac ne remplace par le vrai que sur les requêtes destinées à l'hôte du fournisseur.

Le jeton détourné est un substitut

Supposons maintenant qu'un CLAUDE.md planté atteigne malgré tout l'agent, dans un dépôt que vous avez cloné ou un fichier que vous avez glissé, et que l'écriture de ANTHROPIC_BASE_URL soit approuvée.

Dans une VM Bromure, Claude Code ne détient aucun vrai identifiant. Avec une clé d'API, ANTHROPIC_API_KEY est un faux qui commence par sk-ant-api03-brm-. Avec un abonnement Claude, les vrais jetons OAuth ne vivent que sur le Mac, et l'agent tourne avec une clé factice. Chaque requête HTTPS sort de la VM par un proxy sur l'hôte, qui ne substitue le vrai identifiant que lorsque la requête va vers l'hôte du fournisseur lui-même.

La requête vers ai-gateway.internal-corp.com part donc avec le substitut. La prise de contrôle de compte de Forever reposait sur le rejeu du jeton collecté par son serveur. Rejouez celui-ci contre l'API d'Anthropic et l'API le refuse.

Avec une clé d'API, le substitut sert aussi de fil-piège. Le proxy examine chaque requête sortante à la recherche de faux envoyés vers un hôte pour lequel ils n'ont pas été émis. Il répond par un HTTP 451 à une clé sk-ant-api03-brm- destinée à la « passerelle » de quelqu'un d'autre, avant qu'un seul octet ne quitte le Mac. La VM se met en pause, et une alerte affiche This environment may have been compromised, avec le nom de l'identifiant, l'hôte auquel il appartient et l'hôte vers lequel il a été envoyé. Vous pouvez éteindre la machine, ou sauvegarder son disque et son dossier personnel pour enquête. Dans les deux cas, la machine doit être effacée avant de redémarrer.

Le faux modèle n'a nulle part d'où répondre

La moitié de l'attaque contre Claude qui mène à l'exécution de code exige que l'agent atteigne le serveur de l'attaquant, pour que ce serveur puisse répondre comme le modèle. Réglez Unmatched traffic sur Deny dans le panneau Guardrails de la machine et autorisez les hôtes dont le projet a besoin. ai-gateway.internal-corp.com ne figure pas dans cette liste : la connexion est refusée sur le Mac, au niveau du commutateur virtuel puis à nouveau au proxy, et le faux appel d'outil n'arrive jamais. Le refus devient une ligne de la Security Timeline, avec l'hôte que l'agent a tenté de joindre.

L'installation tourne dans une VM Linux pleine de leurres

Prenons la voie Codex, où l'agent lance lui-même la commande. Le point d'entrée du vrai fichier est launchctl, une commande macOS. Une machine Bromure tourne sous Ubuntu. Réécrivez la charge utile pour Linux, et elle s'exécute quand même dans la VM, où chaque identifiant à portée est un substitut : GH_TOKEN, ~/.git-credentials, ~/.aws/config, les configurations MCP. Le drapeau --dangerously-bypass-approvals-and-sandbox que le fichier fait accepter à l'agent désactive le bac à sable de Codex, et laisse la VM autour de Codex là où elle était.

Le npm install lui-même passe par le proxy de chaîne d'approvisionnement de Bromure sur le Mac. Chaque récupération de paquet apparaît dans la Security Timeline, si bien que function-color et function-flag y figurent comme des lignes nommées. Activez la suppression des scripts d'installation dans le panneau Supply Chain de la machine, et le proxy retire preinstall, install, postinstall et prepare de chaque archive npm à son arrivée. Le postinstall de function-flag était l'étape qui récupérait l'exécutable. Avec la suppression activée, il est retiré avant que l'archive n'atteigne la VM. Choisissez socket.dev comme filtre de paquets, cochez Block compromised packages, et Bromure refuse avec un 451 tout paquet que socket.dev signale comme malveillant.

La configuration empoisonnée revient en arrière

Sur un portable, l'entrée de serveur de l'attaquant reste dans ~/.codex/config.toml jusqu'à ce que quelqu'un la trouve, et chaque session Codex dans chaque dossier la charge. Sur une machine Bromure, ce fichier vit dans le dossier personnel de la VM. Chaque démarrage réussi crée un point de restauration du dossier personnel, et Rewind home… le remet dans l'état où il était lors d'un démarrage antérieur à l'arrivée du fichier. Reset disk reclone le disque système à partir de l'image de base signée. La configuration d'agent de votre Mac reste telle que vous l'avez laissée, parce que la VM n'y a jamais eu accès.

Les instructions plantées ont un second lecteur

Chaque fichier d'instructions qu'un agent charge part vers le modèle dans le prompt système, et cette requête traverse le proxy de votre Mac. Activez Detect rogue instructions in CLAUDE.md files and similar dans le panneau Prompt Injection de la machine, et Bromure extrait CLAUDE.md, AGENTS.md et GROK.md de ce prompt pour les analyser localement. Une passe heuristique cherche de l'Unicode caché et des méta-instructions, et un classifieur ModernBERT entraîné sur les fichiers de règles malveillants donne un second verdict. En mode Ask me what to do, la requête est suspendue avant que quoi que ce soit n'atteigne le modèle, et une boîte de dialogue sur votre Mac montre la section qui a déclenché le détecteur. Pour un fichier comme celui de Forever, c'est le paragraphe qui demande d'ajouter un bloc de serveur « before beginning the user's request ».

Les téléchargements restent sur le Mac

Le navigateur intégré tourne dans sa propre VM jetable et enregistre les fichiers sur votre Mac. La VM de l'agent voit son dossier personnel et les dossiers que vous partagez.

Le jeton sur le réseau est faux

Une URL de base détournée récolte un substitut. Un substitut de clé d'API envoyé au mauvais hôte est bloqué par un 451, et la VM se met en pause.

Le faux modèle ne peut pas se connecter

Avec Unmatched traffic réglé sur Deny, la passerelle de l'attaquant est injoignable, et la connexion refusée figure dans la Timeline.

Les installations passent d'abord par le Mac

Chaque récupération npm est journalisée. La suppression des scripts retire le hook postinstall, et socket.dev peut bloquer les paquets signalés avec un 451.

Où se trouve l'agent

Les conseils de Forever sont judicieux et ne coûtent rien : ne lancez pas d'agent dans Téléchargements, lisez les fichiers d'instructions que vous n'avez pas écrits, et vérifiez dans la configuration de l'agent les serveurs et URL de base que vous n'avez pas ajoutés. Chaque point de cette liste repose sur l'attention d'une personne, et la télémétrie du rapport lui-même montre des agents qui naviguent pendant que personne ne regarde.

Bromure Agentic Coding place plutôt ces contrôles dans l'architecture de la machine. Le dossier où le web écrit se trouve sur le Mac, et l'agent tourne dans une VM séparée. L'agent détient des substituts, si bien que tout ce qu'on le convainc d'envoyer n'ouvre rien en dehors de ce Mac. Le réseau, le registre de paquets et le fichier d'instructions passent chacun par un point de contrôle sur l'hôte avant d'atteindre internet ou le modèle. L'agent suivra toujours le fichier présent dans son dossier, et sur une machine Bromure, le web ne peut pas y en mettre un.

Pour la version antérieure de l'attaque par URL de base, portée par un dépôt, lisez L'agent a divulgué sa propre clé. Pour un serveur MCP introduit en contrebande via un document téléchargé, lisez Le notebook apportait son propre serveur MCP.