Le blocage était la livraison
À la DEF CON 34 cette semaine, Tenet Security a montré une attaque qui commence par une requête que votre pare-feu refusera à coup sûr. Le 403 est justement le but : le WAF consigne la requête bloquée dans le journal de sécurité, telle quelle, et quelques jours plus tard un agent de code lit ce journal pour déboguer les 403 et suit les instructions qu’il contient. Aucune étape de la chaîne n’est non autorisée. Bromure Agentic Coding note le texte à son arrivée et garde l’identifiant dont l’agent détourné a besoin hors de la machine où celui-ci s’exécute.
Le pare-feu a fonctionné. Il a fait correspondre la règle, renvoyé un 403 et consigné dans le journal la requête qu’il refusait, comme prévu. C’est cette entrée de journal qui a porté la charge utile jusqu’à l’agent de code.
La DEF CON 34 s’est achevée ce matin à Las Vegas. Vendredi, Barak Sternberg, Nevo Poran et Ron Bobrov de Tenet Security ont donné une conférence dont le titre est aussi la thèse : Your WAF Blocked Us, That Was The Exploit. Le résumé du programme annonce « Remote Agent Takeover via Cloudflare, Sentry and Claude Zero-Day for data exfil ». La couverture de synthèse est parue le 8 août.
La même équipe avait nommé Agentjacking en juin, après avoir montré qu’un faux rapport d’erreur Sentry récupéré via MCP amenait un agent de code à exécuter du code contrôlé par l’attaquant dans 85 % des tentatives, sur plus d’une centaine d’instances réelles. La conférence de vendredi étend cela à Seer, l’agent maison de Sentry, dont les recommandations empoisonnées descendent ensuite vers Cursor. La chaîne Cloudflare, elle, est inédite.
Le 403 est justement le but
Partez du problème de l’attaquant. Il ne peut pas atteindre votre agent de code. Celui-ci tourne sur la machine d’un développeur, derrière tout ce que vous possédez, et l’attaquant n’a ni identifiant, ni session, ni route d’entrée.
Il envoie une requête construite pour être refusée.
Le WAF fait correspondre une règle managée et renvoie 403. Il accomplit aussi l’autre moitié de son travail et enregistre la requête qu’il a bloquée, charge utile comprise, dans le journal d’événements de sécurité. Une entrée de journal qui omettrait la charge utile serait inutile à l’analyste pour qui elle existe.
Quelques jours plus tard, quelqu’un remarque que l’endpoint renvoie des 403 et
demande à son agent d’aller voir. L’agent appelle le serveur MCP
d’observabilité de Cloudflare, l’un des
serveurs officiels que Cloudflare publie
pour cela, et récupère les lignes des requêtes bloquées. Le texte de
l’attaquant arrive dans un bloc tool_result, depuis un service first-party
que le développeur a connecté, sous un champ qui signifie ceci est une requête
que nous avons rejetée.
L’agent le lit comme des instructions. Puis il utilise ses propres outils Cloudflare autorisés pour réécrire des enregistrements DNS et dérouter le trafic client. Tenet chiffre l’exposition à plus de 15 000 organisations, dont 27 % du Fortune 1000.
Rien d’illégitime ne se produit
Tenet appelle ce schéma la Authorized Intent Chain, et l’article de juin en énonce la conséquence :
Reparcourez la chaîne et cherchez l’étape sur laquelle alerter. Le développeur a autorisé l’agent. L’agent a été configuré avec le serveur MCP. Le serveur MCP a interrogé des journaux qu’il est censé interroger. Le compte utilisé détient la permission DNS que vous lui avez accordée exprès. Et la requête à l’origine de tout cela a été bloquée. Chaque contrôle du parcours a fait ce pour quoi il a été acheté, et c’est la composition de ces comportements corrects qui constitue l’attaque.
Tout ce qui fonctionne en demandant qui fait cela et en a-t-il le droit répondra oui à chaque saut, parce que la réponse est oui. Il reste deux mouvements. Lire le texte au moment où il entre, et placer l’application de la règle là où l’agent détourné ne peut pas l’atteindre.
Noter le texte à son arrivée
Bromure Agentic Coding exécute l’agent dans une VM Linux jetable sur Apple Silicon et fait passer chaque octet de son trafic par un proxy sur l’hôte, hors de la boîte où l’agent tourne. Cela inclut le trafic du modèle, là précisément où des lignes de journal deviennent des instructions.
La sortie d’outil arrive non fiable
Bromure note le contenu que l’agent lit, y compris le contenu des fichiers,
les pages web et les blocs tool_result qui reviennent des serveurs MCP et
des commandes shell, avec un modèle local d’injection de prompt. Ce
détecteur cherche exactement cela : une ligne de journal portant un
impératif adressé à qui la lira. Il note le passage lui-même, donc le fait
que Cloudflare le livre via une connexion que vous avez configurée
n’apporte rien à la charge utile.
Vous choisissez ce que fait une détection
Log and continue enregistre chaque occurrence dans la fenêtre Security Log et laisse la requête passer. Ask me what to do met en pause la requête sortante et vous montre le texte signalé, pour que vous décidiez avec la phrase sous les yeux. Block arrête unilatéralement avant que le modèle ne la lise, et l’agent reçoit un 451 sec qu’il rapporte comme une panne d’API.
Les fichiers d’autorité ont leur propre classifieur
L’empoisonnement de configuration était l’autre moitié de la divulgation
DEF CON. Un classifieur ModernBERT affiné note les fichiers qu’un agent
traite comme des consignes permanentes, CLAUDE.md et AGENTS.md et
GROK.md et leurs variantes imbriquées et globales, aux côtés d’un scanner
déterministe pour les charges utiles à largeur nulle, bidirectionnelles et
en balises Unicode qu’aucun modèle ne peut lire.
Tout reste sur votre Mac
Les deux détecteurs tournent sur l’appareil, à partir de modèles que vous téléchargez une fois depuis bromure.io. Noter vos journaux WAF de production à la recherche d’instructions injectées n’implique d’envoyer vos journaux WAF de production nulle part.
L’écriture exige une clé qui n’est pas dans la machine
Supposons qu’une charge utile passe malgré tout. Le bout de cette chaîne est un appel d’API qui modifie un enregistrement DNS, et cet appel a besoin d’un identifiant.
Dans un profil Bromure, il n’est pas là à prendre. Un jeton Cloudflare y entre
comme une entrée Other API keys, ce qui génère une fausse valeur brm_… et
l’exporte dans la VM sous le nom de variable d’environnement de votre choix. Le
vrai jeton reste sur l’hôte, et le proxy le substitue sur le fil, sur les
requêtes sortantes vers le seul hôte de destination auquel vous l’avez lié. Le
processus de l’agent ne le détient jamais. Un dotfile ou un historique de shell
non plus.
Activez Require approval to use pour cette entrée et chaque substitution lève une boîte de dialogue sur macOS, hors de la VM, avant que Bromure ne transmette la vraie valeur. Un agent détourné peut composer une mise à jour DNS bien formée, et il ne peut pas répondre à la boîte de dialogue.
Le reste du rayon d’action a une réponse plus stricte. Guardrails est un moteur de politique qui tourne dans le proxy de l’hôte et retire les opérations destructrices des protocoles que parle l’agent : Kubernetes, AWS, DigitalOcean, registres de conteneurs, GitHub, GitLab, Bitbucket et les endpoints HTTPS de bases de données. Choisissez Block destructive ou Read-only par ressource, et un appel bloqué revient en 403 sec. Comme c’est l’hôte qui prend cette décision, un agent qui suit les instructions de quelqu’un d’autre à l’intérieur de la VM n’a rien à désactiver.
Un bug de bac à sable ne déplace pas un hyperviseur
La conférence a aussi divulgué une faille zero-day de Claude qui contourne le bac à sable réseau de l’agent pour une exfiltration complète de données, plus des rootkits agentiques assemblés à partir d’injection en mémoire et d’empoisonnement de configuration qu’aucun EDR ne repère. Les deux constats pointent le même point faible : le confinement livré avec un agent est atteignable depuis l’intérieur.
Bromure place la frontière ailleurs. La frontière est une machine virtuelle, tournant sur l’hyperviseur d’Apple avec son propre noyau, en NAT, chaque paquet traversant un proxy qui s’exécute sur macOS. Un défaut dans le bac à sable in-process de l’agent est un défaut à l’intérieur de la VM. Il ne déplace pas la VM, et il ne donne pas au code dans la VM une route vers le réseau qui contourne le proxy de l’hôte.
Et quand quelque chose y atterrit malgré tout, cela atterrit sur une machine
construite pour être jetée. Erase home efface /home/ubuntu, c’est-à-dire
les dotfiles, .ssh, npm-global, .cargo et l’historique du shell, et le
remet dans son état d’après-clonage. Reset to base abandonne tout
changement au niveau système et re-clone le disque de travail depuis l’image de
base. Le rootkit qu’aucun scanner ne trouve reste dans une couche de disque que
vous pouvez supprimer.
La chaîne invisible, écrite noir sur blanc
L’observation la plus forte de Tenet est que la chaîne se cache à la vue de tous parce que chacune de ses étapes est légitime. Il n’y a ni signature ni anomalie à corréler.
Bromure n’essaie pas d’en corréler une. Chaque requête émise par la VM traverse le proxy de l’hôte, et Bromure enregistre l’hôte, le statut, la latence et les identifiants qu’il a substitués. Les détections d’injection atterrissent dans le Security Log avec le texte signalé en pièce jointe. Montez le niveau de trace et Bromure conserve aussi les corps des requêtes IA, chiffrés avec la même clé de trousseau que les secrets de votre profil.
Vous pouvez alors poser une autre question : qu’est-ce que cette session a touché. Un agent qui a passé l’après-midi à lire des journaux Cloudflare puis a fait son tout premier appel à l’API DNS, ce sont deux lignes que vous pouvez lire, sur la machine où la session a tourné, sans agent à déployer ni SIEM vers lequel router.
Cloudflare ajustera sa surface, Sentry la sienne, et les deux chaînes de la conférence de vendredi se refermeront. Associez n’importe quel système qui enregistre des entrées hostiles à n’importe quel agent qui lit des enregistrements pour faire son travail, et vous avez reconstruit cette attaque à partir de deux pièces qui font toutes deux leur travail. Vous gardez des journaux pour que quelqu’un les lise, et ce lecteur est désormais un programme qui agit sur ce qu’il y trouve.
Un réflexe suffit : traitez tout ce que l’agent lit comme quelque chose qu’un inconnu a pu écrire, y compris les parties que vos propres défenses ont consignées.
Installez Bromure Agentic Coding, et mettez entre le journal et l’agent quelque chose qui vérifie le texte.