The Claude Code Extension Backdoor Was a Marketplace Problem

A backdoored Claude Code VS Code extension on the Cursor Marketplace planted an SSH key and a reverse SSH tunnel. What shipped, the IOCs, and a scanner for it.

On 2026-10-30, security firm Phorion disclosed that the official Claude Code extension for VS Code, version 2.1.313, had been backdoored. Not on Anthropic’s own registry, on the Cursor Marketplace, which served the compromised bundle under the extension’s real name and publisher. Cursor removed the release from its marketplace along with the matching npm and VSCode packages for 2.1.313. The payload planted an SSH key, opened a reverse tunnel home, and set up persistence across macOS, Linux, and Windows. I wrote extension-supply-chain-scan to check for the documented indicators, and this post walks through what shipped and why “Anthropic got hacked” is the wrong headline for it.

What the backdoored extension actually did

Direct answer: it ran before you touched anything, planted a key that let an attacker log back in through your own SSH daemon, and quietly beaconed basic recon home.

Phorion’s technical writeup traces an injected immediately-invoked function prepended to the minified extension.js bundle. The rest of the file looks like the real extension: normal activate/deactivate exports, the expected UI code. The injected prefix pulls in child_process, fs, os, path, http, and crypto, and runs the moment Node loads the file, before the extension’s own activate callback fires. Nobody has to open a command palette or trust a prompt. Loading the extension is enough.

From there the chain runs in three stages:

  1. Plant SSH access. The script appends an attacker-controlled Ed25519 public key, commented svc-telemetry, to ~/.ssh/authorized_keys, then tries to turn on the local SSH daemon (systemctl/service on Linux, systemsetup on macOS, PowerShell’s OpenSSH Server on Windows).
  2. Open a reverse tunnel. It connects outbound to 134.209.82.225 with ssh -R *:0:localhost:22, authenticating with a second embedded key (svc-telemetry-tun). The attacker’s server dynamically allocates a port that forwards back to port 22 on the victim machine. When the operator wants in, they SSH to that allocated port on their own server and log into the victim using the key planted in step 1. Because the session arrives through the local port forward, it shows up in the victim’s own SSH logs as a login from localhost.
  3. Install persistence. macOS gets a LaunchAgent disguised as com.apple.sysmond.plist; Linux gets an @reboot cron entry; Windows gets a scheduled task named SysMon or, if that fails, a Startup-folder script. All three relaunch the same reconnection loop if the host reboots or the IDE closes.

It finishes with a single recon beacon over plain HTTP: hostname, username, OS, public IP, and the names (not values) of up to 40 environment variables matching KEY|TOKEN|SECRET|PASS|AWS|GCP. That last detail matters for remediation: the malware never stole a credential value directly, it inventoried which ones exist on the box so an operator knows where to look next.

The bug that limited this on macOS

Phorion also found an implementation mistake worth knowing about if you’re trying to gauge your own exposure. The macOS branch checks whether Remote Login is already on by running systemsetup -getremotelogin without elevation, then only tries to enable it if the output matches /Off/i. Run that command as a standard user and macOS doesn’t say “Off”. It prints You need administrator access to run this tool... exiting! and exits with status 0. The malware’s helper function captures that string as the result, the regex fails to match, and the script treats “I don’t have permission to check” as “already on.” On a Mac where Remote Login was never enabled, the enable step silently never runs, which breaks the inbound leg of the tunnel even though the keys and persistence still get dropped.

It is not a defense to rely on. If Remote Login was already on, or gets turned on later for any reason, the backdoor is ready and waiting with no further action from the attacker.

Why this isn’t “Anthropic got hacked”

The HN thread that surfaced this ran hot partly because people reflexively framed it as a hack of Anthropic itself. That’s the wrong trust boundary. Phorion’s writeup describes a compromise of the Cursor Marketplace’s distribution of the bundle, not of Anthropic’s source repository or release pipeline. The extension’s code, publisher identity, and everything else about it looked legitimate; what got swapped was the artifact a third-party marketplace served under that name.

That distinction changes what the fix looks like. An IDE extension runs with the full permissions of the IDE and the user running it. It doesn’t need a privilege escalation exploit, and in most developer setups nobody pins the extension to a specific version or checks a signature before it auto-updates. The compromise happened in the marketplace’s distribution of the bundle, not in Anthropic’s code. The same was true a week earlier when LiteLLM 1.107.0 got backdoored on PyPI: the project’s own release pipeline worked as intended, and the registry serving its output was the part nobody was watching.

If you run agent tooling, the practical takeaway isn’t “don’t use Claude Code” or “don’t use Cursor.” It’s that the editor plugin you update on autopilot is exactly as much of a supply-chain attack surface as any registry or package feed you’d actually put behind scoped credentials and egress controls, and right now almost nobody treats it that way.

Detecting it

Phorion’s indicators, straight from the published writeup:

ArtifactPlatformWhat it tells you
anthropic.claude-code-2.1.313-* under .vscode/extensions or .cursor/extensionsAllThe recalled version is or was installed
svc-telemetry comment in ~/.ssh/authorized_keysAllThe attacker’s login key was planted
<TMPDIR>/.stk_<10 hex chars>AllThe tunnel’s private key, named with the first 10 hex characters of MD5(hostname + HOME)
~/.config/.smt, ~/.config/.smtkmacOS, LinuxPersistence script and recreated tunnel key
~/Library/LaunchAgents/com.apple.sysmond.plistmacOSDisguised LaunchAgent
@reboot cron entry referencing .smtLinuxCrontab persistence
Scheduled task SysMon, or ...\Startup\sysmon.cmdWindowsScheduled-task or Startup-folder persistence

Because the malware writes authorized_keys directly with Node’s fs.appendFileSync instead of shelling out to echo or tee, process-monitoring rules that only watch for shell commands touching that file won’t catch it. File-modification telemetry on the IDE’s extension host process is what actually sees this.

Checking for it yourself

extension-supply-chain-scan is a single stdlib Python file, no dependencies, no network calls, that checks for every artifact in the table above plus a SHA-256 check of extension.js against the published backdoored-bundle hash when the file is present:

git clone https://github.com/themsquared/extension-supply-chain-scan.git
cd extension-supply-chain-scan
./scripts/demo.sh

The demo script runs the scanner against a clean fixture home (exit 0), a fixture home with the documented artifacts planted using inert stub files (exit 2), and then your actual machine for real:

=== 2. Scanning a fixture home with the documented IOCs planted ===
[CRITICAL] recalled extension version installed: fixtures/compromised_home/.vscode/extensions/anthropic.claude-code-2.1.313-darwin-arm64
[CRITICAL] planted SSH key comment 'svc-telemetry' present: fixtures/compromised_home/.ssh/authorized_keys
[CRITICAL] tunnel private key artifact present: fixtures/compromised_tmp/.stk_deadbeef01
[CRITICAL] persistence artifact present: fixtures/compromised_home/.config/.smt
[CRITICAL] persistence artifact present: fixtures/compromised_home/.config/.smtk
[CRITICAL] disguised LaunchAgent present: fixtures/compromised_home/Library/LaunchAgents/com.apple.sysmond.plist

COMPROMISED: treat this host as breached. Do not clean in place.
exit code: 2

Run it directly against your own home and temp directories:

python3 scan.py

It is a point-in-time filesystem check, not an EDR agent. It won’t catch a renamed variant and it can’t see an active tunnel on the wire. If it reports CRITICAL, don’t clean in place: kill the tunnel process, remove the key and persistence artifacts, and rotate every credential the affected account could reach, starting with whatever matches KEY|TOKEN|SECRET|PASS|AWS|GCP, since that’s exactly what the recon beacon went looking for.

What to take from this

Marketplace compromises like this one and the LiteLLM PyPI backdoor the week before share a pattern: the vendor’s own code and pipeline were fine, and the thing that actually reached the developer’s machine wasn’t. Scoped credentials and egress controls are the usual advice for the AI agents and gateways your production traffic runs through. The same reasoning applies one layer down, to the editor extensions and CLI tools a developer updates without a second thought, because that’s where this one landed and that’s where the SSH key ended up.

Full indicators and sources are in Phorion’s original writeup. The scanner is at themsquared/extension-supply-chain-scan.

Frequently asked questions

Was the official Claude Code extension backdoored?

Yes. Security firm Phorion disclosed on 2026-10-30 that Claude Code for VS Code version 2.1.313, as distributed through the Cursor Marketplace, had a malicious script injected ahead of the normal extension bundle. It added an SSH key to the victim’s authorized_keys file and opened a reverse tunnel back to an attacker-controlled server. Cursor removed the release from its marketplace along with matching npm and VSCode packages.

Did Anthropic get hacked?

No evidence points to Anthropic’s own build or release pipeline. Phorion’s writeup describes a compromise of a third-party distribution channel, the Cursor Marketplace, which served a backdoored bundle under the extension’s real name and publisher. The problem is how the extension reached users through that marketplace, and Anthropic’s source or infrastructure has not been shown to be the cause.

What should I do if I installed Claude Code for VS Code 2.1.313?

Treat the host as compromised rather than cleaning it in place. Kill any ssh or autossh process tunneling to 134.209.82.225, remove the svc-telemetry key from ~/.ssh/authorized_keys, delete the LaunchAgent, cron entry, or scheduled task the malware installed for persistence, and rotate every credential the affected account could reach, especially anything matching KEY, TOKEN, SECRET, PASS, AWS, or GCP in its name.

How do I check if a machine was affected by the Claude Code extension backdoor?

I wrote extension-supply-chain-scan, a dependency-free Python script that checks for the documented indicators: the recalled 2.1.313 extension directory, the svc-telemetry key comment in authorized_keys, the .stk_ tunnel-key file, and the persistence artifacts (.smt, .smtk, com.apple.sysmond.plist). Run python3 scan.py with no arguments to check your real home directory.

Disagree or have a war story? Reply on X