# 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.

- Canonical URL: https://webofmike.com/claude-code-extension-backdoor/
- Author: Mike Moore (https://webofmike.com/about/)
- Published: 2026-11-15
- Last modified: 2026-11-15
- Tags: AI Agents, Security, AI Tools, Platform Engineering
- Cite as: Mike Moore, "The Claude Code Extension Backdoor Was a Marketplace Problem", Web of Mike (webofmike.com), 2026-11-15. https://webofmike.com/claude-code-extension-backdoor/


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](https://github.com/themsquared/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](https://phorion.io/blog/offical-claude-code-vsx-compromise/) 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](/litellm-pypi-backdoor/): 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:

| Artifact | Platform | What it tells you |
|---|---|---|
| `anthropic.claude-code-2.1.313-*` under `.vscode/extensions` or `.cursor/extensions` | All | The recalled version is or was installed |
| `svc-telemetry` comment in `~/.ssh/authorized_keys` | All | The attacker's login key was planted |
| `<TMPDIR>/.stk_<10 hex chars>` | All | The tunnel's private key, named with the first 10 hex characters of `MD5(hostname + HOME)` |
| `~/.config/.smt`, `~/.config/.smtk` | macOS, Linux | Persistence script and recreated tunnel key |
| `~/Library/LaunchAgents/com.apple.sysmond.plist` | macOS | Disguised LaunchAgent |
| `@reboot` cron entry referencing `.smt` | Linux | Crontab persistence |
| Scheduled task `SysMon`, or `...\Startup\sysmon.cmd` | Windows | Scheduled-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](https://github.com/themsquared/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:

```bash
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:

```bash
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](https://phorion.io/blog/offical-claude-code-vsx-compromise/). The scanner is at [themsquared/extension-supply-chain-scan](https://github.com/themsquared/extension-supply-chain-scan).

