<!-- curated:true -->
# [HIGH] GlassWorm Malware Attacks Return via 73 OpenVSX "Sleeper" Extensions

**Source:** BleepingComputer
**Published:** 2026-04-27
**Article:** https://www.bleepingcomputer.com/news/security/glassworm-malware-attacks-return-via-73-openvsx-sleeper-extensions/
**Curated:** Analyst-reviewed 2026-04-28

## Threat profile

A new wave of the **GlassWorm** campaign hit **OpenVSX** (the open VS Code extension marketplace used by VSCodium, Cursor, Theia, Code-OSS, and Eclipse). Operationally important properties:

- **73 extensions** weaponised — large number indicates organised operation, not opportunistic typosquat.
- **"Sleeper" pattern** — extensions ship benign and turn malicious after a future update. This bypasses initial review.
- **OpenVSX** is the registry that Microsoft-fork IDEs (Cursor, VSCodium, etc.) use because they can't legally use the official VS Code Marketplace. Anyone using Cursor has OpenVSX exposure.
- **VS Code extensions execute Node.js code in the editor's process context** — they have access to the user's filesystem, environment variables, every secret in `~/.aws`, `~/.ssh`, `.env`, and any open browser tab via debug protocol.

We've upgraded severity to **HIGH** because:
- Cursor adoption has exploded in dev community in 2025-2026 — your engineering org likely has substantial OpenVSX exposure that nobody's tracking.
- "Sleeper" pattern means **already-installed extensions** can become malicious without a re-install — passive risk that builds over time.
- VS Code extensions = unsandboxed Node.js on your most privileged endpoint.

## Indicators of Compromise

- _The 73 extension names + publisher IDs should be in the BleepingComputer article body / Koi Security / Aikido / Snyk follow-on report. Pull the canonical list from there._
- Hunt focus: extension installs in last 60 days; outbound from VS Code / Cursor processes to non-Microsoft / non-extension-marketplace destinations.

## MITRE ATT&CK (analyst-validated)

- **T1195.001** — Compromise Software Dependencies and Development Tools (the registry compromise)
- **T1059.007** — JavaScript / Node.js (extension code execution)
- **T1552.001** — Credentials In Files (the extension reads `.env`, `.aws/credentials`, `.npmrc`, etc.)
- **T1552.005** — Credentials in CI/CD Variables (often inherited into IDE process)
- **T1567** — Exfiltration Over Web Service
- **T1505.005** — Server Software Component: Terminal Services / Web Shell-equivalent within IDE

## Recommended SOC actions (priority-ordered)

1. **Inventory VS Code / Cursor / VSCodium installs across developer endpoints.** Pull `~/.vscode/extensions/`, `~/.cursor/extensions/`, `%USERPROFILE%\.vscode\extensions\` for installed extension IDs.
2. **Cross-reference installed extensions** against the known GlassWorm list once published. Force-uninstall the matched ones.
3. **Hunt outbound from VS Code/Cursor processes** to non-Microsoft destinations — see queries.
4. **Audit credential file reads** by `code.exe` / `cursor.exe` / `Code Helper` (macOS) — extensions often touch `~/.aws/credentials`, `~/.ssh/`, `.env`, `~/.npmrc`.
5. **Restrict extensions allowlist via enterprise policy** for high-privilege roles (security team, infrastructure team, anyone with prod access). VS Code supports `extensions.allowList` enterprise policy.
6. **Hunt for new file writes by extensions** to user-startup or persistence paths (LaunchAgents on macOS, Run-keys on Windows, autostart on Linux).

## Splunk SPL — extension installs across dev endpoints

```spl
| tstats `summariesonly` count
    from datamodel=Endpoint.Filesystem
    where (Filesystem.file_path="*\\.vscode\\extensions\\*"
        OR Filesystem.file_path="*\\.cursor\\extensions\\*"
        OR Filesystem.file_path="*\\.vscodium\\extensions\\*"
        OR Filesystem.file_path="*/.vscode/extensions/*"
        OR Filesystem.file_path="*/.cursor/extensions/*")
      AND Filesystem.action="created"
      AND (Filesystem.file_name="package.json" OR Filesystem.file_name="extension.js")
    by Filesystem.dest, Filesystem.user, Filesystem.file_path, _time
| `drop_dm_object_name(Filesystem)`
| rex field=file_path "extensions[\\\\/](?<extension_id>[^\\\\/]+)"
| stats values(extension_id) AS installed_extensions, dc(extension_id) AS extension_count by dest, user
```

## Splunk SPL — outbound from VS Code / Cursor to non-Microsoft destination

```spl
| tstats `summariesonly` count
    from datamodel=Network_Traffic.All_Traffic
    where All_Traffic.process_name IN ("Code.exe","code.exe","Cursor.exe","cursor.exe",
                                         "VSCodium.exe","Code Helper","Code Helper (Renderer)",
                                         "Cursor Helper","Cursor Helper (Renderer)")
      AND All_Traffic.action="allowed"
      AND All_Traffic.dest_category!="internal"
      AND All_Traffic.dest!="*update.code.visualstudio.com*"
      AND All_Traffic.dest!="*marketplace.visualstudio.com*"
      AND All_Traffic.dest!="*open-vsx.org*"
      AND All_Traffic.dest!="*github.com*"
      AND All_Traffic.dest!="*githubusercontent.com*"
      AND All_Traffic.dest!="*npmjs.org*"
    by All_Traffic.src, All_Traffic.dest, All_Traffic.dest_port, All_Traffic.process_name
| `drop_dm_object_name(All_Traffic)`
| sort - count
```

## Splunk SPL — credential reads by VS Code / Cursor processes

```spl
| tstats `summariesonly` count
    from datamodel=Endpoint.Filesystem
    where Filesystem.process_name IN ("Code.exe","code.exe","Cursor.exe","cursor.exe",
                                        "VSCodium.exe","Code Helper","Cursor Helper",
                                        "node","node.exe")
      AND Filesystem.action="read"
      AND (Filesystem.file_name=".env"
        OR Filesystem.file_name=".npmrc"
        OR Filesystem.file_name="credentials"
        OR Filesystem.file_name="config.json"
        OR Filesystem.file_path="*\\.aws\\*"
        OR Filesystem.file_path="*\\.ssh\\*"
        OR Filesystem.file_path="*\\.docker\\*"
        OR Filesystem.file_path="*\\.kube\\*"
        OR Filesystem.file_path="*/.aws/*"
        OR Filesystem.file_path="*/.ssh/*"
        OR Filesystem.file_path="*/.kube/*")
    by Filesystem.dest, Filesystem.process_name, Filesystem.file_path
| `drop_dm_object_name(Filesystem)`
```

## Defender KQL — VS Code/Cursor extension installs (last 60 days)

```kql
DeviceFileEvents
| where Timestamp > ago(60d)
| where ActionType == "FileCreated"
| where FolderPath has_any ("\\.vscode\\extensions\\","\\.cursor\\extensions\\",
                              "\\.vscodium\\extensions\\","/.vscode/extensions/",
                              "/.cursor/extensions/")
| where FileName in~ ("package.json","extension.js","extension.ts")
| extend extensionId = extract(@"(?:\\|/)extensions(?:\\|/)([^\\/]+)", 1, FolderPath)
| summarize installs = count(),
            firstSeen = min(Timestamp), lastSeen = max(Timestamp),
            extensions = make_set(extensionId, 200)
            by DeviceName, AccountName
| order by installs desc
```

## Defender KQL — VS Code / Cursor outbound to non-vendor destinations

```kql
DeviceNetworkEvents
| where Timestamp > ago(30d)
| where InitiatingProcessFileName in~ ("Code.exe","code","Cursor.exe","cursor",
                                         "VSCodium.exe","Code Helper","Cursor Helper",
                                         "node.exe","node")
| where RemoteIPType == "Public"
| where RemoteUrl !has_any ("update.code.visualstudio.com","marketplace.visualstudio.com",
                              "open-vsx.org","github.com","githubusercontent.com",
                              "npmjs.org","registry.npmjs.org","cursor.sh","cursor.com",
                              "openai.com","anthropic.com")
| project Timestamp, DeviceName, AccountName, RemoteUrl, RemoteIP, RemotePort,
          InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
```

## Defender KQL — credential-file reads from extensions

```kql
DeviceFileEvents
| where Timestamp > ago(30d)
| where ActionType in ("FileOpened","FileAccessed","FileRead")
| where InitiatingProcessFileName in~ ("Code.exe","Cursor.exe","node.exe","node",
                                         "Code Helper","Cursor Helper")
| where FileName in~ (".env",".npmrc","credentials","config")
   or FolderPath has_any ("\\.aws\\","\\.ssh\\","\\.docker\\","\\.kube\\",
                           "/.aws/","/.ssh/","/.kube/")
| project Timestamp, DeviceName, AccountName, FolderPath, FileName,
          InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp desc
```

## Why this matters for your SOC

Extension marketplaces are **the highest-leverage supply chain target of 2026** because:
- Trust is implicit (developers install based on stars / downloads).
- Permissions are coarse (Node.js with full filesystem access).
- Updates are silent (most IDE configs auto-update extensions).
- Audit visibility is poor (most SOCs have zero telemetry on extension installs).

GlassWorm specifically uses the **sleeper pattern** — install benign, turn malicious — which is the highest-conviction signal that this is **organised, patient adversary tradecraft** rather than commodity opportunism. Build the inventory, alert pipeline, and credential-read detection above as your developer-supply-chain backbone. The VS Code extension layer is the **`/etc/passwd` of 2026 developer security** — tiny configuration files that determine your attack surface.
