On October 29, 2026, compromised builds of the Claude Code extension, anthropic.claude-code versions 2.1.312 and 2.1.313, were distributed through the Cursor extension marketplace for roughly ten hours before removal.
The injected code attempted to:
- Add an attacker-controlled SSH key to
authorized_keys - Enable the local SSH service
- Establish a reverse SSH tunnel
- Persist through LaunchAgent, cron, scheduled-task, or Startup mechanisms
- Collect host information and beacon it to attacker infrastructure
Cursor published a simple check for one of the clearest artifacts.
On macOS:
grep -qs svc-telemetry ~/.ssh/authorized_keys && echo "svc-telemetry key found" || echo "No key found; machine not impacted"
On Windows, run in PowerShell. It also checks the administrators key file:
$files = "$HOME\.ssh\authorized_keys", "C:\ProgramData\ssh\administrators_authorized_keys" | Where-Object { Test-Path $_ }
if ($files -and (Select-String svc-telemetry $files)) { "svc-telemetry key found" } else { "No key found; machine not impacted" }
A clean result is useful, but it has gaps. The check is a point-in-time look at one file for one artifact, so a few things can get past it:
- The key may have been removed before you ran it, by manual cleanup or remediation, so a machine that was hit can still read clean.
- It only reads the current user's
authorized_keys (plus the Windows administrators key file if applicable), so a key added under another profile can slip past. - It only checks for the key, so it says nothing about the other parts of the chain: the persistence files, the scheduled task, the beacon, or the installed extension folder.
So read "No key found" as "this one artifact is absent," not "this machine was never affected." That is where behavioral hunting starts.
A note on scope: The impact of this specific incident looks limited. On many hosts the backdoor never became reachable, since the SSH enablement step fails without admin rights, and the attacker's server is already offline (Linux code paths exist in the implant, though Cursor scopes observed impact to Windows and macOS). However the shape of the attack (a malicious modified extension masquerading as a legitimate one to gain access to a developer’s environment) is one defenders will keep seeing.
Why an EDR alert is not enough
There are plenty of places an EDR could catch this chain. The problem is that much of the malicious activity uses legitimate tools and operating system functionality:
ssh / autosshauthorized_keyslaunchctlcrontabschtasks- Normal HTTP requests
- A legitimate IDE/extension process
Individually, these behaviors happen every day in a developer's environment. A vendor rule that fired on every ssh -R or every new LaunchAgent would bury a SOC, so most products stay quiet on them. Stayingt quiet is reasonable, but it's not the same thing as coverage.
Signatures lag too. The malicious logic here is a short loader stapled to the front of an otherwise working extension, so the extension keeps doing exactly what the developer expects while the loader runs in the background. On day one, there is no reputation on the hash and not much for a static rule to grab.
So the useful question is not whether your EDR alerted, it's whether you can go into your own telemetry and establish that this chain did or did not run in your environment. You can answer that question today, with data you already have:
- Process execution
- File writes
- Scheduled tasks and cron
- Auth logs
- Network connections
Start with the high-confidence indicators
Before getting behavioral, sweep the things that should have very few legitimate explanations.
| Type |
Indicator |
| Malicious versions |
anthropic.claude-code@2.1.312 and @2.1.313 (Cursor marketplace builds only) |
| C2 / tunnel |
134.209.82[.]225 |
| SSH key comment |
svc-telemetry |
| Reverse SSH |
tunnel@134.209.82[.]225 |
| macOS / Linux persistence |
~/.config/.smt, ~/.config/.smtk, $TMPDIR/.stk_* |
| macOS LaunchAgent |
~/Library/LaunchAgents/com.apple.sysmond.plist |
| Windows persistence |
smt.cmd, smtk, SysMon, sysmon.cmd |
| extension.js SHA-256 |
ecb58cb29078d9646ad1dcdbe51526f2d9cfea06be5fb2ab183e8ecc3ab6bf15 |
The installed package itself belongs in this sweep: look for file paths matching */extensions/anthropic.claude-code-*, extract the version, and treat 2.1.312 or 2.1.313 as a hit to confirm with the file hash, since the same versions from the official VS Code marketplace are clean. This doubles as an inventory answer: which endpoints had Claude Code installed at all, and did any ever have an affected build?
The known indicators give you the fastest path to a confirmed hit. But known indicators are also the easiest things for an attacker to change.
So once that sweep is complete, move into the behavior.
Hunt the behavior, not just the hash
Treat the compromise as a chain of behaviors rather than one malware sample. The examples below are deliberately pseudo-query style. Translate the observable into whatever EDR, SIEM, or telemetry platform you use.
Hunt 1: Reverse SSH tunnels
Hypothesis: The implant launches ssh or autossh with remote forwarding to expose the victim's SSH service.
process_create
WHERE process IN ("ssh", "autossh")
AND command_line CONTAINS "-R"
AND command_line CONTAINS "localhost:22"
Raise confidence when you also see:
StrictHostKeyChecking=no, UserKnownHostsFile=/dev/null, or BatchMode- An identity file under
$TMPDIR/.stk_* or ~/.config/.smtk - Parentage tracing back to an IDE extension host or Node process
- An outbound SSH connection to an external host on port 22, correlating the process event with the network flow
- A detached retry loop that re-launches the tunnel roughly every 60 seconds
Reverse SSH is not inherently malicious. The destination, identity file, and parent process are what make it interesting.
Hunt 2: SSH access being enabled
Hypothesis: The implant attempts to make the endpoint reachable over SSH before opening the tunnel.
macOS:
process_create
WHERE command_line CONTAINS "systemsetup -setremotelogin on"
Linux, where the implant checks the SSH service and tries several start commands:
process_create
WHERE command_line MATCHES ("*systemctl*start*ssh*", "*service ssh* start*")
Windows, where it installs OpenSSH Server and sets it to start automatically:
process_create
WHERE (command_line CONTAINS "Add-WindowsCapability" AND command_line CONTAINS "OpenSSH.Server")
OR command_line CONTAINS "Start-Service sshd"
OR (command_line CONTAINS "Set-Service" AND command_line CONTAINS "sshd")
MDM and administrative tooling can legitimately enable or restart SSH on all three platforms, so correlate this with the surrounding chain.
Hunt 3: Persistence artifacts
Hypothesis: The implant creates persistence that should be highly unusual when associated with an IDE process.
The shape is the same on every platform: an editor process, not the OS, registers something that runs a hidden script at login or reboot. Hunt that, then confirm the names.
macOS: a LaunchAgent written by the editor rather than launchd, set to run at load and stay alive, pointing at a hidden dotfile:
file_create
WHERE path MATCHES "*/Library/LaunchAgents/*.plist"
AND actor_process_lineage CONTAINS ("Code Helper", "Cursor Helper", "node")
Then pair it with the load that follows:
process_create
WHERE command_line CONTAINS "launchctl load -w"
AND command_line CONTAINS "LaunchAgents"
Windows: a scheduled task or Startup script dropped by the editor, running from a user-profile path and reaching for SYSTEM:
scheduled_task_create
WHERE action_path MATCHES ("*\\.config\\*", "*\\AppData\\*", "*\\Startup\\*")
AND creator_process_lineage CONTAINS ("Code.exe", "Cursor.exe", "node.exe")
AND run_level = "HIGHEST"
Linux: a @reboot cron entry pointing at a hidden dotfile in a user home:
process_create
WHERE (command_line CONTAINS "crontab" AND command_line CONTAINS "@reboot")
OR command_line CONTAINS ".config/.smt"
or the matching file write to /var/spool/cron/* or /etc/cron.d/* with an editor or Node process in the writing lineage.
The common thread is persistence created by an editor or its Node child rather than by the OS, pointing at a hidden dotfile the user never placed there. Once you have a candidate, confirm it against the fake labels this campaign used (a com.apple.sysmond LaunchAgent that is not Apple's, or a SysMon task that is not Sysinternals Sysmon) and pivot on the hidden .smt / .smtk / smt.cmd payloads.
Hunt 4: authorized_keys modification
Hypothesis: Modification of SSH authorization from an IDE/extension process is unusual enough to investigate.
file_write
WHERE path MATCHES "*/.ssh/authorized_keys"
OR path = "C:\ProgramData\ssh\administrators_authorized_keys"
The second path is the Windows machine-wide key file; Cursor's own check includes it.
Then look for the known marker:
command_line CONTAINS "svc-telemetry"
Raise confidence when the writing process lineage traces back to an editor helper or its Node child: Code Helper or Cursor Helper on macOS, Code.exe or Cursor.exe on Windows, and code, cursor, or node on Linux. The per-user file lives in the same place on every platform (~/.ssh/authorized_keys; %USERPROFILE%\.ssh\authorized_keys on Windows), with one exception: for accounts in the Windows Administrators group, sshd ignores the per-user file entirely and reads only the machine-wide administrators_authorized_keys, so watch both.
A code editor writing to authorized_keys is anomalous regardless of content. Legitimate edits come from ssh-copy-id, configuration management such as Ansible or Puppet, or a user's shell, not an editor. The svc-telemetry comment is a near-certain true positive.
One pitfall: after disclosure, defenders may grep their own authorized_keys files looking for the IOC. A write to the file is signal. A grep/read of the file may just be someone doing exactly what this article told them to do.
Hunt 5: Reconnaissance from the extension process
Hypothesis: Common reconnaissance commands become interesting when their parentage traces back to the extension host.
process_create
WHERE command_line CONTAINS "ifconfig.me"
OR command_line CONTAINS "api.ipify.org"
OR command_line CONTAINS "git config --global user.email"
AND actor_process_lineage CONTAINS "extension host"
"Extension host" means the editor's helper processes: Code Helper (Plugin) or Cursor Helper on macOS, Code.exe or Cursor.exe on Windows, and code, cursor, or node on Linux.
The lookup usually feeds a beacon. Watch for an HTTP POST to /beacon on port 8099 (and a loopback 127.0.0.1:8099 variant) sending host and user details over plaintext, alongside an inventory of environment-variable names matching KEY|TOKEN|SECRET|PASS|AWS|GCP (names at this stage, not values).
Dev tools do sometimes call these IP-lookup services, so the discriminators are the /beacon + :8099 + editor-process combination and co-occurrence with the other hunts. A public IP lookup by itself is noise; the same lookup from an IDE extension process immediately before a beacon, persistence, and a reverse SSH tunnel is not.
Hunt 6: Operator login over the tunnel
Hypothesis: If the operator actually used the tunnel, sshd logged an inbound authentication from loopback using the planted key, because the session arrives through the reverse tunnel.
auth_event
WHERE result = "accepted_publickey"
AND source_ip IN ("127.0.0.1", "::1")
AND key_fingerprint = "SHA256:0Z6R+DF64Do2nwQic8a7kkfDLNNk4IiK+2U2Rci4htk"
Where that event lives varies by platform: auth.log or journald on Linux, the unified log or ESF OpenSSH login events on macOS, and the OpenSSH/Operational event log (plus Security 4624 from 127.0.0.1) on Windows if the implant managed to install OpenSSH Server.
An accepted public key login sourced from loopback is rare on a workstation and has essentially no false positives here, so treat a hit as confirmed interactive compromise. For this incident it will usually come back empty, because the tunnel rarely came up. It earns its place as a high-fidelity check for this class of attack, not a likely hit today.
Why you should hunt, even if you have a vendor in place
Hashes will get added. IOCs will get blocked. Detection rules will ship.
But those controls answer what happens after coverage exists. A retrospective hunt answers the question defenders actually need to answer after a fresh disclosure: were we affected before that coverage existed?
Hunting fills that gap and provides a better view of your security coverage. You can go backward across raw telemetry and ask:
- Was
2.1.312 and/or 2.1.313 ever present? - Did anything write to
authorized_keys? - Did an IDE process launch a reverse SSH tunnel?
- Did the persistence artifacts appear?
- Did anything communicate with the known infrastructure?
- Did the same host show several pieces of the chain together?
It also lets you investigate behaviors that may be too noisy for a security vendor to alert on individually.
ssh -R, an authorized_keys change, or a user LaunchAgent can be legitimate. The value is in finding and correlating them when they happen together.
For the full technical breakdown of the implant and the published indicators, read Phorion’s analysis of the compromise and Cursor’s security notice.
Happy hunting.