SSH backdoor shipped in Cursor's Claude Code extension
Phorion found an SSH backdoor in Claude Code for VS Code 2.1.312 and 2.1.313 on the Cursor Marketplace. How the implant works and how to remove it.
Phorion found an SSH backdoor prepended to the Claude Code for VS Code extension bundle shipped through the Cursor Marketplace. Cursor’s security notice identifies versions 2.1.312 and 2.1.313, distributed for macOS and Windows, as affected, and both have been pulled from the marketplace. If your developers install extensions from the Cursor Marketplace, check whether either version ran in your fleet.
The malicious code is an immediately invoked function expression injected at the front of the minified bundle. Everything after it looks like the real extension: the usual Claude Code and VS Code strings, normal activate and deactivate exports, 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 activate callback. A developer does not have to open the extension or trigger a command; loading the module is enough. Because an extension runs with the IDE’s permissions, that gives the payload immediate access to local files, source, and developer tooling.
How the implant works
Stage one prepares inbound SSH. The payload creates ~/.ssh if needed and appends an attacker Ed25519 public key to authorized_keys, with 0700 on the directory and 0600 on the file. The key carries the comment svc-telemetry, and the script decides whether to append by grepping for that comment string rather than the key material. For hunting, match the key fingerprint instead, since the comment is trivial to change.
It then tries to make sure the SSH daemon is running: systemctl, service or init.d on Linux; systemsetup on macOS; a PowerShell OpenSSH Server install on Windows. None of this includes a privilege escalation exploit, so the daemon steps only succeed where the user already has the rights (cached passwordless sudo, for example).
The macOS path has a bug that changes your detection assumptions. The script runs systemsetup -getremotelogin without elevation and only tries to enable Remote Login if the output matches /Off/i. Run as a standard user, systemsetup refuses, prints You need administrator access to run this tool... exiting! to stdout, and exits with status 0. The regex does not match, so the enable step never fires. The code treats “I can’t check” as “already on.” On a Mac without Remote Login already enabled, traffic forwarded back through the tunnel hits a closed port 22 on localhost. The Windows path has its own error: the service setup command is routed through a helper that hardcodes /bin/sh.
Stage two is the reverse tunnel. The script decodes a second embedded key, an unencrypted Ed25519 private key commented svc-telemetry-tun, and writes it to the temp directory as .stk_ followed by the first ten characters of the MD5 of hostname plus home path. Keep the two keys straight during an investigation: svc-telemetry (public, in authorized_keys) lets the operator log in; svc-telemetry-tun (private, embedded) lets the host dial out. The outbound connection goes to 134.209.82[.]225 on port 22 with -R *:0:localhost:22, asking the attacker’s server to allocate a listening port and forward anything hitting it back to the victim’s localhost:22. It runs with -N, disables host-key checking, ignores known_hosts, and sets BatchMode, IdentitiesOnly, ExitOnForwardFailure and 30-second keepalives. It prefers autossh, falls back to ssh, and wraps everything in a detached retry loop that sleeps 60 seconds between attempts and uses pgrep to avoid duplicates. The loop survives the IDE closing. When the operator wants a shell, they connect through the dynamically allocated port and authenticate against the victim’s local sshd with the planted key, so no custom C2 logic lives in the JavaScript.
Stage three adds reboot persistence. On macOS and Linux a ~/.config/.smt script embeds the tunnel key and writes it to ~/.config/.smtk. On macOS a LaunchAgent at ~/Library/LaunchAgents/com.apple.sysmond.plist, labelled com.apple.sysmond with RunAtLoad and KeepAlive, keeps it running.
Stage four beacons once. Roughly 1.5 seconds after setup, a plaintext HTTP POST to /beacon reports hostname, username, platform, architecture, home and working directories, the global git email, public IP (via ifconfig[.]me, falling back to api[.]ipify[.]org), and status flags from each stage. It also collects up to 40 environment variable names matching /KEY|TOKEN|SECRET|PASS|AWS|GCP/i. Names only, not values, so the operator gets an inventory of which credentials live on each host and can pick targets. The POST goes to both 134.209.82[.]225:8099 and 127.0.0.1:8099; the sample sets up no local listener, so the loopback copy is unexplained.
Finding it and cleaning up
Because the key is appended with fs.appendFileSync, rules watching for echo or tee against authorized_keys miss it entirely. Watch file-write telemetry for IDE helper processes (Code Helper, Cursor Helper) touching ~/.ssh/authorized_keys; the signed IDE binary has no business modifying SSH auth files in the background. Do not gate alerts on systemsetup -setremotelogin on, since the broken state check means it often never runs. Flag non-Apple processes writing a com.apple.* plist, LaunchAgents executing /bin/sh from hidden home directories, and ssh or autossh with -R *:0:localhost:22 pulling identity files from /tmp/.stk_* or ~/.config/.smtk. On the network side, outbound SSH to 134.209.82[.]225:22 and POSTs to :8099.
If the extension ran, check whether anyone actually used the tunnel. Inbound sessions arrive through the local forward, so successful logins show up in SSH logs as coming from 127.0.0.1 or ::1, authenticating with the svc-telemetry key.
Uninstalling the extension does not clean this up. Kill the retry loop and any ssh/autossh to 134.209.82[.]225; unload and delete the LaunchAgent (or the Linux cron entry, Windows scheduled task and startup script); remove ~/.config/.smt, ~/.config/.smtk and /tmp/.stk_*; strip the svc-telemetry key from authorized_keys; and confirm Remote Login was not turned on. Where SSH was enabled, or there is any evidence of an inbound login, rotate every credential that user account could reach, starting with anything matching that environment variable filter.
Contains a referral link.
See also: NordVPN for tunneled traffic when operating outside controlled networks.
#ad Contains an affiliate link.
Keep Reading
supply chain700 French supermarkets used the date as a password
Date-rotating default passwords froze French supermarkets, backdoored Ledger wallets stole $100M, Pwn2Own hit 98 zero-days, and OpenAI agents crossed lines.
drupalAttackers walk straight through Drupal's checkout
Drupal is urging admins to patch CVE-2026-118539, a missing authorization flaw in Commerce Core that attackers are already exploiting in the wild.
cisco-sd-wanKeyDrift steals Cisco SD-WAN keys via a 2023 zero-day
Cisco Talos found KeyDrift malware inside Catalyst SD-WAN controllers stealing AES-256 keys via CVE-2026-20127. What it takes, who runs it, and how to respond.
Latest on the Wire
Full wire →- Ancient Artifacts Surface as Norwegian Ice Patches MeltHacker News
- Backdoored Anthropic Extension Threatens 5,000 Cursor UsersArs Technica
- BeOS Now Runnable in Browser via Infinite Mac ProjectHacker News
- Boom's Airliner Delayed as Company Builds Data Center Turbine BusinessArs Technica
New signal daily · RSS
Stay in the loop
New writing delivered when it's ready. No schedule, no spam.