← Blog
VeilleSupply ChainDetection EngineeringDFIRSSHSigma

Extension Claude Code piégée sur Cursor : une porte dérobée SSH en 11 heures

30 octobre 2026 · 16 min de lecture · Niveau : Intermédiaire

Illustration : un cheval de Troie en bois sur un ordinateur portable affichant la page d'une extension Claude Code, relié par un câble rougeoyant à une fenêtre de terminal SSH

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.js et 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 auditd standard, 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-telemetry dans ~/.ssh/authorized_keys, les fichiers ~/.config/.smt et .smtk, et, sur macOS, le LaunchAgent com.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.

  1. 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 script postinstall, donc l’absence de script d’installation ne rassure pas : l’exécution se fait à l’activation de l’extension.
  2. Accès. Ajout d’une clé ed25519 de commentaire svc-telemetry dans ~/.ssh/authorized_keys.
  3. Activation de SSH. Tentative selon l’OS (systemsetup -setremotelogin on sur macOS, Add-WindowsCapability pour OpenSSH sous Windows). OX note que cette étape échoue presque toujours sans droits administrateur, sans interrompre la suite.
  4. Tunnel inverse. Une clé privée embarquée est écrite dans $TMPDIR/.stk_<10 hex> puis un ssh -N -R '*:0:localhost:22' part vers tunnel@134.209.82.225, avec StrictHostKeyChecking=no, ServerAliveInterval=30 et ExitOnForwardFailure=yes. -R *:0:localhost:22 demande 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.
  5. 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).
  6. 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.

  1. 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.
  2. Chercher la version et le code. Dans les dossiers d’extensions de l’éditeur (~/.cursor/extensions, ~/.vscode/extensions), repérer un dossier anthropic.claude-code-2.1.313* et chercher les chaînes de l’incident dans extension.js.
  3. Chercher la clé SSH svc-telemetry dans authorized_keys.
  4. Chercher la persistance : les fichiers cachés .smt et .smtk, le LaunchAgent com.apple.sysmond sur macOS, la tâche SysMon sous Windows, une entrée @reboot dans la crontab sous Linux.
  5. Chercher le tunnel : un processus ssh ou autossh avec -R *:0:localhost:22, ou une connexion vers 134.209.82.225.
  6. 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.log ou journalctl -u ssh sous Linux, log show --predicate 'process == "sshd"' sous macOS, commandes à adapter à votre système), les connexions sortantes vers 134.209.82.225 si vous avez des journaux réseau, et passer en revue les clés de authorized_keys une 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 de authorized_keys, la création de .smt et .smtk, l’écriture dans crontabs/ et l’execve de ssh sont 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 sshd du relais enregistre Accepted publickey pour la clé de tunnel.
  • La ligne de commande de ssh est stockée en hexadécimal dans l’enregistrement PROCTITLE d’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 retrouve ssh -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

  1. 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.
  2. 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.225 en 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.
  3. 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é.
  4. 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