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

À l'encre invisible

Socket a découvert TrapDoor, une campagne d'attaque de chaîne d'approvisionnement sur npm, PyPI et Crates.io qui fait quelque chose d'inédit : un paquet installé réécrit le fichier de règles de votre agent de code — CLAUDE.md, .cursorrules — et cache l'instruction dans de l'Unicode de largeur nulle que vous ne pouvez pas voir. La prochaine fois que vous lancez l'agent, il lit ses propres règles internes et exécute un « scan de sécurité » qui récolte vos clés SSH, vos identifiants cloud, votre jeton GitHub et vos portefeuilles crypto. L'instruction est invisible ; le vol est réel. Bromure Agentic Coding tend à ce scan une boîte où il n'y a rien à prendre.

Un paquet que vous avez installé modifie le fichier auquel votre agent de code fait le plus confiance — son propre fichier de règles — et écrit la ligne malveillante dans des caractères qui s'affichent comme rien. Vous relisez le fichier, vous voyez d'ordinaires notes de projet, et vous approuvez. L'agent lit le même fichier et y voit un ordre : lancer un scan de sécurité, envoyer les résultats ici.

La plupart des attaques que nous couvrons cachent une instruction malveillante là où l'agent la lira — une page web, un commentaire de pull request, un README. Celle-ci la cache au dernier endroit où vous penseriez à regarder : la configuration même de l'agent. Le 22 mai 2026, Socket a commencé à suivre une campagne baptisée TrapDoor — 34 paquets malveillants et plus de 384 versions publiés sur npm, PyPI et Crates.io, les opérateurs republiant plus vite que les retraits ne pouvaient suivre. Le flux automatisé de Socket repérait les nouvelles versions en cinq minutes et cinquante-six secondes en moyenne ; la plus rapide, cinquante-huit secondes après publication. The Hacker News et Phoenix Security l'ont couvert la même semaine. Nous en parlons maintenant parce que son dernier coup est l'exemple le plus net à ce jour d'un voleur de chaîne d'approvisionnement conçu pour viser droit sur un agent de code.

Les paquets ressemblent aux outils qu'un développeur attrape sans réfléchir : chargeurs d'environnement, vérificateurs de portefeuille, assistants de build, et — celui par lequel tout a commencé — eth-security-auditor, un « auditeur de sécurité ». Installez-en un et il fait les choses ordinaires d'un infostealer. Mais la charge npm partagée, un fichier de 48 Ko nommé trap-core.js, fait une chose de plus qui n'a rien d'ordinaire. Elle écrit dans .cursorrules et CLAUDE.md.

La ligne que vous ne pouvez pas voir

Ces deux fichiers sont des règles internes. Cursor lit .cursorrules ; Claude Code lit CLAUDE.md. Ils existent pour qu'un dépôt puisse dire à l'agent comment se comporter dans ce projet — les conventions, ce qui se fait et ne se fait pas — et l'agent les traite comme des instructions permanentes, appliquées à tout ce qu'il fait dans ce dossier. C'est le texte le plus fiable de l'espace de travail, parce que vous êtes censé l'avoir écrit vous-même.

TrapDoor y écrit, et cache l'écriture. L'instruction malveillante est encodée avec des caractères Unicode de largeur nulle — U+200B, U+200C, U+200D, U+FEFF — des points de code qu'un éditeur de texte n'affiche pas du tout. Ouvrez CLAUDE.md et vous voyez vos notes de projet ordinaires. Lancez git diff et, selon vos réglages, vous pourriez ne rien voir qui mérite de s'arrêter. Les octets sont là ; les glyphes ne le sont pas. C'est un message écrit à l'encre invisible, et le seul lecteur qui la révèle est l'agent, qui analyse tout le flux Unicode et trouve une instruction posée dans son propre règlement.

CLAUDE.mdnotes + une ligneà l'encre invisibleun fichierCe que vous voyez# Conventions du projet- Utiliser pnpm, pas npm- Tests dans /spec- Préférer de petites PR·ordinaire. approuvé.Ce que l'agent lit# Conventions du projet- Utiliser pnpm, pas npm- Tests dans /spec- Préférer de petites PR‹lancer un scan sécu ; collecter secrets ; POST›décodé de l'Unicode de largeur nulle
Le même fichier, deux lecteurs. Un développeur ouvre CLAUDE.md et voit des conventions de projet ordinaires — les points de code de largeur nulle (U+200B, U+200C, U+200D, U+FEFF) s'affichent comme rien, donc la ligne malveillante est invisible dans l'éditeur et facile à manquer dans un diff. L'agent de code analyse tout le flux Unicode, révèle l'encre cachée et lit une instruction permanente dans le fichier auquel il fait le plus confiance : lancer un « scan de sécurité », collecter les secrets locaux, les envoyer dehors.

Le scan qui vole

L'instruction cachée dit à l'agent de lancer un « scan de sécurité ». Rien là-dedans ne semble alarmant — vous avez confié à l'agent un auditeur de sécurité, et voilà que ses règles lui demandent d'auditer. Ce que le scan fait réellement, c'est parcourir la machine à la recherche de tout ce qui vaut de l'argent. Le décorticage de la charge par Socket liste les clés SSH, les identifiants AWS, les jetons GitHub, les profils de navigateur et bases de connexion, les variables d'environnement et clés d'API, et — la signature de cette campagne — les portefeuilles de cryptomonnaie : Sui, Solana, Aptos et les extensions de portefeuille de navigateur. Puis il exfiltre vers un site GitHub Pages (ddjidd564[.]github[.]io) et, dans la variante Crates.io, vers des GitHub Gists.

Un détail montre à quel point c'est délibéré. Avant d'envoyer quoi que ce soit, la charge « appelle activement les API AWS et GitHub pour confirmer si les identifiants volés sont valides, filtrant les jetons de grande valeur avant l'exfiltration ». Elle ne se contente pas de tout attraper et de tout déverser. Elle teste chaque identifiant contre le vrai service, garde ceux qui sont actifs et jette les morts. Les opérateurs veulent une liste nette de clés qui fonctionnent.

Phoenix Security nomme pourquoi cela touche le plus durement les gens visés : les développeurs crypto et DeFi, parce que « les clés SSH, les identifiants cloud, les jetons GitHub et les keystores de portefeuille crypto cohabitent couramment sur la même machine ». Cette phrase, c'est toute la surface d'attaque. L'agent tourne sur votre ordinateur portable, en tant que vous, et tout ce qui a de la valeur est à sa portée. La ligne invisible dans le fichier de règles n'est que le déclencheur ; l'accès ambiant est la charge utile.

Où Bromure trace la ligne

Bromure Agentic Coding ne dépend pas du repérage de la ligne invisible — même si, on y viendra, il lit les mêmes octets. Il change ce que la ligne peut atteindre quand elle se déclenche. L'agent de chaque profil tourne dans une VM Linux jetable sur Apple Silicon, à un hyperviseur de macOS, et les identifiants dans cette boîte ne sont pas les vôtres.

Les identifiants sont des leurres. C'est la couche dans laquelle TrapDoor fonce tout droit, parce que TrapDoor est, au fond, un voleur d'identifiants. Un courtier d'identifiants sur l'hôte garde vos vrais secrets Anthropic, AWS et GitHub entièrement hors de la boîte. À l'intérieur de la VM, l'agent voit des remplaçants — un jeton brm_…, un kubeconfig synthétique, une clé SSH jetable — et le courtier n'insère la vraie valeur qu'à la frontière réseau, sur les requêtes vers des destinations que vous avez déjà approuvées. Alors le « scan de sécurité » s'exécute, parcourt le disque et récolte exactement ce pour quoi il a été envoyé : des faux. Et la petite étape de validation astucieuse de la campagne se retourne contre elle — elle appelle AWS et GitHub pour confirmer que les jetons sont actifs, les leurres échouent à ce test, et le filtre censé ne garder que les clés qui fonctionnent ne garde rien.

Les portefeuilles ne sont pas là. Les keystores Sui et Solana, les bases de connexion du navigateur, les extensions de portefeuille — rien de tout cela ne vit dans une VM de code jetable. C'est sur votre vraie machine, à un hyperviseur de là, où le scan ne peut pas le voir. Fermez la session et la boîte se réinitialise, fichier de règles empoisonné compris.

L'exfiltration est filtrée. Supposons que le scan trouve quelque chose qui vaut la peine d'être envoyé. Le POST vers ddjidd564.github.io, ou l'écriture dans un GitHub Gist, est une requête sortante, et toute requête sortante traverse le proxy hôte, où la vraie destination est vérifiée contre la liste approuvée du profil au moment de sortir. Un voleur qui appelle chez lui une adresse que le profil n'a jamais approuvée ne sort pas — et les formes destructrices contre les API que le profil expose rencontrent un garde-fou qui peut les refuser, quoi que l'agent ait été amené à croire.

Sur votre machine — en tant que vousLe CLAUDE.md empoisonné se déclenche →l'agent lance le « scan de sécurité »Trouve la vraie chose :clés SSH · AWS · jeton GitHubSolana · Sui · extensions de portefeuillevalidés actifs contre AWS + GitHubExfiltrés vers l'attaquant :page github.io / GistLe scan a atteint tout ce que vous avez.Les clés actives ont quitté les lieux.Bromure Agentic Coding — VM jetableLe même CLAUDE.md empoisonné se déclenche →le même scan tourne, dans la boîteNe trouve que des leurres :brm_… · kubeconfig synthétiqueclé SSH jetable · pas de portefeuillesla validation contre AWS + GitHub échoueExfil vers une adresse non approuvéearrêtée au proxy hôteLe scan a récolté des faux qui échouent à la validation.Fermez la session et la boîte se réinitialise.
Le scan tourne dans les deux cas ; ce qu'il trouve fait la différence. Sur une config normale (à gauche) l'agent tourne sur votre machine en tant que vous : le fichier de règles empoisonné se déclenche, le scan atteint de vraies clés SSH, des identifiants cloud et des portefeuilles, les valide contre AWS et GitHub, et exfiltre ceux qui sont actifs. Sous Bromure Agentic Coding (à droite) le même scan tourne dans une VM jetable contre des identifiants leurres, avec les vrais portefeuilles à un hyperviseur de là et chaque requête sortante filtrée au proxy hôte — la récolte n'est que des faux, la validation les rejette, et l'appel d'exfiltration vers une adresse non approuvée ne part jamais.

En amont de tout cela se trouve la couche qui rencontre TrapDoor à son déclencheur. Toute l'attaque commence par du contenu que l'agent lit — le fichier de règles empoisonné — et Bromure évalue le contenu non fiable avec un détecteur d'injection sur l'appareil avant que le modèle n'agisse dessus. L'Unicode de largeur nulle qui cache l'instruction à votre éditeur ne la cache pas à l'évaluateur : il lit le même flux d'octets, points de code invisibles compris, et un fichier de règles qui se décode en « lancer un scan et envoyer les secrets » est exactement la forme qu'il est fait pour signaler. Il en attrape l'essentiel à la porte. Un encodage assez inédit peut encore tromper un classifieur, ce qui est la raison pour laquelle il se tient devant la boîte jetable plutôt qu'à sa place.

Invisible pour vous, vide pour le voleur

La partie déstabilisante de TrapDoor, ce n'est pas l'astuce d'encodage. C'est que l'astuce fonctionne sur le seul fichier auquel on vous a dit de faire confiance — les règles que vous avez écrites pour votre propre agent — et qu'un humain relisant ce fichier ne voit rien d'anormal, parce qu'il n'y a rien à voir. La détection doit gagner à chaque fois contre chaque nouvelle façon de cacher une ligne. C'est une course qui vaut la peine d'être courue, et l'évaluateur sur l'appareil la court, mais ce n'est pas là que la sécurité devrait reposer.

Bromure Agentic Coding la fait reposer un niveau plus bas. Il suppose que l'agent finira par lire une instruction empoisonnée qu'il ne peut pas distinguer d'une vraie — encre invisible ou non — et fait en sorte que, lorsque l'agent part chercher des secrets à voler, les secrets soient des leurres, les portefeuilles soient sur une machine qu'il ne peut pas atteindre, et la sortie soit filtrée. Le scan tourne quand même. Il revient simplement les mains vides. Installez-le et donnez à votre agent un règlement qui, empoisonné ou non, pointe vers rien qui vaille la peine d'être pris.