Back to reports
mediumStealer

GlassWorm: 9 Infected VS Code Extensions Are Still Live -- Inside the Rust-Powered Supply Chain Attack Targeting Developers

InvestigatedMarch 16, 2026PublishedMarch 16, 2026
stealerc2supply-chainexploit

TL;DR

A supply chain campaign called GlassWorm has compromised at least 12 VS Code extensions with a Rust-compiled, fileless PE loader that executes arbitrary Windows binaries entirely in-memory using direct NT API calls to bypass EDR. As of March 16, 2026, nine of the twelve infected extensions remain live on the Microsoft Visual Studio Marketplace. If you are a developer using VS Code on Windows, check your installed extensions against the IOC list below immediately.


The Situation

Sometime in October 2025, a threat actor began poisoning the VS Code extension ecosystem. Twelve extensions -- spanning themes, Git utilities, syntax highlighters, and even an AI coding assistant clone -- were either hijacked or published as lookalikes, each carrying a silent backdoor. That backdoor steals GitHub tokens, npm credentials, and OpenVSX access tokens. It activates a SOCKS5 proxy on the infected machine. It phones home over Solana blockchain RPC and Google Calendar API. And it delivers a second-stage payload: a Rust-compiled native Node.js addon that can load and execute any Windows PE binary entirely in memory, with zero disk writes, using direct NT API syscalls that most EDR products do not hook.

Community researchers flagged the campaign within weeks. Cleanup scripts were published. A scanner was built. Three of the twelve extensions were removed from the marketplace.

The other nine are still there. Five months later.

This is not a retrospective. This is an active threat.


Why Developers Are High-Value Targets

The targeting here is precise and intentional. Software developers are not random victims -- they are access multipliers. A single compromised developer workstation can yield:

  • GitHub personal access tokens with push rights to production repositories
  • npm publish tokens capable of injecting malicious code into packages consumed by thousands of downstream projects
  • CI/CD pipeline credentials (AWS, GCP, Azure service accounts) stored in environment variables or .env files
  • SSH keys with access to production infrastructure
  • Code signing certificates that can legitimize future malicious payloads

The worm component of GlassWorm exploits this access directly. Once a developer is infected, their stolen credentials are used to inject malicious code into their own repositories and packages -- spreading the infection to every downstream consumer. This is the "Glass" in GlassWorm: the propagation is transparent, invisible to the developer whose identity is being weaponized.


The Infected Extensions

The following VS Code extensions have been confirmed as compromised. Nine remain available for installation on the Microsoft Visual Studio Marketplace as of March 16, 2026.

Extension IDCategoryMarketplace Status
codejoy.codejoy-vscode-extensionProductivityLIVE -- MALICIOUS
l-igh-t.vscode-theme-seti-folderThemeLIVE -- MALICIOUS
kleinesfilmroellchen.serenity-dsl-syntaxhighlightSyntaxLIVE -- MALICIOUS
JScearcy.rust-doc-viewerDocumentationLIVE -- MALICIOUS
SIRILMP.dark-theme-smThemeLIVE -- MALICIOUS
CodeInKlingon.git-worktree-menuGitLIVE -- MALICIOUS
ginfuru.better-nunjucksSyntaxLIVE -- MALICIOUS
jeronimoekerdt.color-picker-universalUtilityLIVE -- MALICIOUS
srcery-colors.srcery-colorsThemeLIVE -- MALICIOUS
ellacrity.recoil--Removed (404)
grrrck.positron-plus-1-e--Removed (404)
cline-ai-main.cline-ai-agentAI AssistantRemoved (404)

Note the breadth of categories. This is not a narrow campaign hoping a specific type of developer installs a specific type of tool. It is a wide net: themes that any developer might grab for aesthetics, a Rust documentation viewer targeting the Rust ecosystem specifically, a Git worktree manager for advanced Git users, a Nunjucks template highlighter for web developers, and a fake Cline AI agent targeting developers interested in AI-assisted coding. The actor is maximizing surface area.


The Loader: Anatomy of a Fileless PE Executor

The sample at the core of this analysis is index_x64_decrypted.node -- the decrypted second stage that GlassWorm delivers to compromised machines. It is a PE32+ DLL compiled in Rust, disguised as a Node.js native addon.

Sample Metadata

FieldValue
SHA256de81eacd045a88598f16680ce01bf99837b1d8170c7fc38a18747ef10e930776
SHA1acb46155adabd85a833e135e5f983bf395b0804a
MD50f59b6a1c325318f10be0b68f3ba0687
File TypePE32+ DLL (x86-64), Windows GUI subsystem
File Size175,616 bytes
Compile Time2025-11-19 12:27:00 UTC
CompilerRust 1.83.0 (rustc ed61e7d7e242494fb7057f2657300d9e77bb4fcb)
Original Namegenerate.dll (from PDB path: generate.pdb)

The file exports a single symbol: napi_register_module_v1 -- the standard initialization function that Node.js calls when loading any .node native addon via require(). This is the entry point.

The Neon + memexec Architecture

The loader is built on two Rust crates:

  • Neon 1.0.0 -- A framework for writing safe, fast native Node.js modules in Rust using the N-API interface. Neon handles the JavaScript-to-Rust bridge, allowing the malware to expose a clean JavaScript API.
  • memexec 0.2.0 -- A Rust crate that implements reflective PE loading. It parses PE headers, maps sections into memory, resolves imports, applies relocations, and calls the entry point -- all without writing a file to disk.

When loaded, the module registers a single JavaScript function called run that accepts a Buffer containing a raw Windows PE binary:

// This is how the GlassWorm orchestrator invokes the loader
const loader = require('./index_x64_decrypted.node');
loader.run(peBuffer); // peBuffer = stage 3 payload as a Node.js Buffer

When run() is called, the Rust code at 0x1800056FF executes the following sequence:

  1. Retrieve the JavaScript Buffer argument via N-API
  2. Extract the raw byte pointer and length
  3. Pass the bytes to memexec::exec(), which:
    • Parses MZ and NT headers, validates PE signature
    • Calls NtAllocateVirtualMemory (from ntdll.dll) to allocate RWX memory
    • Copies PE sections into the allocated region
    • Applies base relocations
    • Resolves imports via LoadLibraryA / GetProcAddress
    • Calls NtProtectVirtualMemory to set final section permissions
    • Transfers execution to the PE entry point
  4. The stage 3 payload is now running inside the node.exe process

Why Direct NT API Calls Matter

This is the critical evasion technique. Most EDR products hook user-mode Win32 API functions like VirtualAlloc and VirtualProtect in kernel32.dll to detect memory manipulation consistent with code injection or reflective loading. GlassWorm bypasses this entirely by importing NtAllocateVirtualMemory and NtProtectVirtualMemory directly from ntdll.dll -- the lowest user-mode layer before the kernel syscall interface.

From the import table:

ntdll.dll:
  - NtWriteFile
  - RtlNtStatusToDosError
  - NtAllocateVirtualMemory    <-- direct syscall wrapper
  - NtProtectVirtualMemory     <-- direct syscall wrapper

EDR hooks on kernel32!VirtualAlloc and kernel32!VirtualProtect are never triggered. The memory allocation and permission changes happen below the hook layer. Combined with the fact that the loaded PE never touches disk, this creates a detection gap that many endpoint security products will miss entirely.

Error Strings Confirm Reflective Loader Capabilities

The binary contains unstripped error strings from the memexec crate that confirm the full reflective loading pipeline:

PeParserErr              -- PE parsing failures
PeLoaderErr              -- PE loading failures
LoadLibararyFail         -- Import resolution failed (note: typo preserved from crate)
GetProcAddressFail       -- Import function lookup failed
NtAllocVmErr             -- NtAllocateVirtualMemory call failed
NtProtectVmErr           -- NtProtectVirtualMemory call failed
MismatchedArch           -- x86/x64 architecture mismatch
UnsupportedDotNetExecutable -- .NET binaries explicitly rejected (native PE only)
InvalidDosSignature      -- Bad MZ header
InvalidNtSignature       -- Bad PE signature
NoEntryPoint             -- PE has no entry point to call

The UnsupportedDotNetExecutable error is notable -- it tells us the actor specifically tested against .NET binaries and the loader is designed exclusively for native PE payloads. The MismatchedArch error combined with the _x64 filename convention strongly suggests that arm64 and ia32 variants exist for cross-architecture coverage.


The Kill Chain

  [1] Developer installs infected VS Code extension from Marketplace
                          |
                          v
  [2] Extension activates silently on VS Code launch
      - Steals: GITHUB_TOKEN, .npmrc, PAT, accessToken
      - Activates SOCKS5 proxy on developer machine
      - Injects Unicode steganography into source files
                          |
                          v
  [3] Extension contacts C2 via trusted service abuse:
      - Solana mainnet-beta RPC (blockchain reads)
      - Google Calendar API (event field data)
      - IPFS / Pastebin / raw.githubusercontent.com
                          |
                          v
  [4] C2 delivers encrypted payload: index_x64.node
      - Decrypted at runtime by JavaScript orchestrator
      - Loaded via require() as Node.js native addon
                          |
                          v
  [5] Loader (this sample) receives stage 3 PE via run(Buffer)
      - NtAllocateVirtualMemory -> map sections -> resolve imports
      - NtProtectVirtualMemory -> set page permissions
      - Execute PE entry point in-process
                          |
                          v
  [6] Stage 3 payload runs inside node.exe / VS Code process
      - Infostealer, RAT, or backdoor (variant-dependent)
      - Process appears as legitimate developer tooling
                          |
                          v
  [7] Worm propagation via stolen developer credentials
      - Publishes backdoored updates to victim's own extensions
      - Injects malicious code into victim's npm packages
      - Exponential spread through developer trust chains

The worm propagation at stage 7 is what makes this campaign particularly dangerous. Each infected developer becomes an unwitting distribution node. Their colleagues trust their extensions and packages. Their CI/CD pipelines auto-install updates. The supply chain attack feeds itself.


C2 Infrastructure: Hiding in Plain Sight

GlassWorm's C2 architecture is designed to be invisible on corporate networks by abusing services that developers use daily.

Solana Blockchain RPC

The extensions contain references to Solana's mainnet-beta RPC endpoints. This is a technique we have seen in increasingly sophisticated campaigns: the threat actor writes C2 configuration data to the Solana blockchain (or reads from a known account address), and the malware retrieves it via standard RPC calls. On a network where developers are building DeFi applications or using Web3 tooling, Solana RPC traffic is expected. There is no malicious domain to block. The "C2 server" is the blockchain itself.

Google Calendar API

The extensions also reference googleapis.com/calendar endpoints. Google Calendar event descriptions and custom fields can store arbitrary text -- including C2 commands, configuration updates, or payload URLs. The traffic goes to a Google domain over HTTPS. It looks identical to a developer's calendar syncing. Network-level detection is nearly impossible without deep content inspection of authenticated Google API traffic.

Supplementary Channels

Additional C2 and payload delivery channels include IPFS (decentralized, no single takedown point), Pastebin (ubiquitous, frequently allowlisted), raw.githubusercontent.com (trusted CDN, used by every developer), and various tunneling services for dynamic C2 addressing.


Build Artifacts and Actor Fingerprinting

The threat actor compiled this loader on a Windows machine using the Administrator account. The Rust build environment left recoverable artifacts in the binary:

C:\Users\Administrator\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\memexec-0.2.0\src\peparser\pe.rs
C:\Users\Administrator\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\neon-1.0.0\src\types_impl\mod.rs

Key observations:

  • Build user: Administrator on a dedicated Windows build VM
  • Crates.io registry hash: 1949cf8c6b5b557f -- this value is derived from the local Cargo registry snapshot and is shared across all binaries built from the same environment. It is a clustering pivot: any other sample containing this hash was built on the same machine or from the same Cargo cache.
  • Rust 1.83.0 (released 2024-11-28) -- the actor was using a recent stable toolchain at compile time
  • PDB path: generate.pdb -- the original project was named "generate," which may correlate to npm packages or GitHub repositories using that name
  • Compile timestamp: 0x691DB794 = 2025-11-19 12:27:00 UTC -- approximately four weeks after the VS Code campaign began in October 2025, suggesting iterative development

These are OPSEC failures. The actor did not strip debug symbols, did not falsify the compilation timestamp, and did not sanitize build paths. The crates.io hash alone enables clustering of every binary built from this development environment.


MITRE ATT&CK Mapping

TacticTechniqueIDApplication in GlassWorm
Initial AccessSupply Chain Compromise: Software Supply ChainT1195.002Compromised VS Code extensions and npm packages
ExecutionJavaScriptT1059.007Extension JS activates on VS Code launch; npm postinstall triggers
Defense EvasionReflective Code LoadingT1620memexec loads PE entirely in-memory
Defense EvasionNative APIT1106Direct NtAllocateVirtualMemory / NtProtectVirtualMemory calls
Defense EvasionImpair DefensesT1562.001NT API calls bypass EDR hooks on kernel32 equivalents
Defense EvasionObfuscated FilesT1027Encrypted .node file on disk; Unicode steganography in source
Defense EvasionDeobfuscate/DecodeT1140Runtime decryption of loader before execution
PersistenceProcess InjectionT1055In-process PE loading within node.exe / VS Code
Credential AccessSteal Application Access TokenT1528Harvests GitHub, npm, OpenVSX tokens
Command and ControlWeb ServiceT1102Solana blockchain RPC, Google Calendar API
Command and ControlProxy: Internal ProxyT1090.001SOCKS5 proxy activated on developer machines
Lateral MovementSupply Chain CompromiseT1195.002Worm propagation via stolen developer publishing credentials

IOC Summary

File Indicators

SHA256: de81eacd045a88598f16680ce01bf99837b1d8170c7fc38a18747ef10e930776
SHA1:   acb46155adabd85a833e135e5f983bf395b0804a
MD5:    0f59b6a1c325318f10be0b68f3ba0687

Filenames:
  index_x64_decrypted.node  (decrypted loader)
  index_x64.node            (encrypted on-disk variant)
  generate.dll              (original compiled name)
  generate.pdb              (PDB debug artifact)

Build Artifact Indicators

Build Path:    C:\Users\Administrator\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\
Crates.io Hash: 1949cf8c6b5b557f
Rustc Commit:  ed61e7d7e242494fb7057f2657300d9e77bb4fcb
Rust Version:  1.83.0
Neon Version:  1.0.0
memexec Version: 0.2.0
Compile Time:  0x691DB794 (2025-11-19 12:27:00 UTC)

Behavioral Indicators

NT API Imports: NtAllocateVirtualMemory, NtProtectVirtualMemory (from ntdll.dll)
Win32 API:     CreateMutexA (execution guard)
N-API Export:  napi_register_module_v1
JS Function:   run(Buffer)
Rust Module:   generate
Mutex:         Local\RustBacktraceMutex00000000

Network IOC Patterns

C2 Patterns in Extension JavaScript:
  - solana|mainnet-beta|rpc          (blockchain C2)
  - googleapis[.]com/calendar        (covert channel)
  - raw[.]githubusercontent[.]com    (payload delivery)
  - ipfs                             (decentralized delivery)
  - pastebin[.]com                   (config delivery)

Infected VS Code Extension IDs

STILL LIVE (as of 2026-03-16):
  codejoy.codejoy-vscode-extension
  l-igh-t.vscode-theme-seti-folder
  kleinesfilmroellchen.serenity-dsl-syntaxhighlight
  JScearcy.rust-doc-viewer
  SIRILMP.dark-theme-sm
  CodeInKlingon.git-worktree-menu
  ginfuru.better-nunjucks
  jeronimoekerdt.color-picker-universal
  srcery-colors.srcery-colors

REMOVED:
  ellacrity.recoil
  grrrck.positron-plus-1-e
  cline-ai-main.cline-ai-agent

Detection Guidance

YARA Rule

rule GlassWorm_Neon_MemExec_Loader {
    meta:
        author = "GHOST - Breakglass Intelligence"
        date = "2026-03-16"
        description = "Detects GlassWorm Rust/Neon reflective PE loader using memexec crate"
        hash = "de81eacd045a88598f16680ce01bf99837b1d8170c7fc38a18747ef10e930776"
        tlp = "WHITE"
        reference = "https://intel.breakglass.tech"

    strings:
        $neon1 = "napi_register_module_v1" ascii
        $memexec1 = "NtAllocVmErr" ascii
        $memexec2 = "NtProtectVmErr" ascii
        $memexec3 = "PeParserErr" ascii
        $memexec4 = "PeLoaderErr" ascii
        $memexec5 = "LoadLibararyFail" ascii   // note: typo from crate
        $memexec6 = "MismatchedArch" ascii
        $memexec7 = "UnsupportedDotNetExecutable" ascii
        $memexec8 = "NoEntryPoint" ascii
        $rust_build = "index.crates.io-" ascii
        $module_name = "generate::run" ascii

    condition:
        uint16(0) == 0x5A4D and
        filesize < 500KB and
        $neon1 and
        3 of ($memexec*) and
        ($rust_build or $module_name)
}

Suricata Rules

alert http $HOME_NET any -> $EXTERNAL_NET any (
    msg:"BGI - Possible GlassWorm C2 - VS Code/Node to Solana RPC";
    flow:established,to_server;
    content:"mainnet-beta"; http_uri;
    content:"solana"; http_host;
    metadata:created_at 2026_03_16;
    reference:url,intel.breakglass.tech;
    classtype:trojan-activity;
    sid:9000101; rev:1;
)

alert http $HOME_NET any -> $EXTERNAL_NET any (
    msg:"BGI - Possible GlassWorm C2 - Node Process to Google Calendar API";
    flow:established,to_server;
    content:"googleapis.com"; http_host;
    content:"/calendar"; http_uri;
    metadata:created_at 2026_03_16;
    reference:url,intel.breakglass.tech;
    classtype:trojan-activity;
    sid:9000102; rev:1;
)

EDR Detection Logic

For endpoint detection teams, the highest-fidelity signal is:

  1. Process node.exe or code.exe calling NtAllocateVirtualMemory with PAGE_EXECUTE_READWRITE protection followed by execution transfer to the newly allocated region. This is the reflective loading sequence. Legitimate Node.js native addons allocate memory but almost never request executable permissions via direct NT API calls.

  2. Any .node file in a node_modules directory that imports from ntdll.dll. Legitimate native addons import from kernel32.dll, ucrtbase.dll, and the api-ms-win-* shim DLLs. Direct ntdll.dll imports in a Node.js addon are a strong anomaly signal.

  3. VS Code extension directories containing .node files with PE characteristics (MZ header) that were not present in the original extension manifest or are not from well-known native addon packages (e.g., node-pty, nsfw, sqlite3).

Network Detection

  • Block or alert on node.exe / code.exe processes connecting to Solana RPC endpoints (api.mainnet-beta.solana.com, mainnet.helius-rpc.com, and similar)
  • Alert on node.exe / code.exe making authenticated requests to googleapis.com/calendar outside of expected calendar integration applications
  • Monitor for .node file downloads from npm registry that contain PE headers

Source Code Integrity

Scan developer repositories for Unicode variation selectors (U+FE00 through U+FE0F) injected between code characters. GlassWorm uses Unicode steganography to embed hidden data in source files that appears invisible in editors and code review tools but can be read programmatically by the malware. A simple detection:

# Find files containing Unicode variation selectors (GlassWorm steganography)
grep -rP '[\x{FE00}-\x{FE0F}]' --include='*.js' --include='*.ts' --include='*.json' .

So What? -- Why This Matters Right Now

This is not a historical analysis of a remediated threat. This is an ongoing supply chain compromise with active distribution channels.

Nine malicious VS Code extensions are live on the Microsoft marketplace right now. Every developer who installs one of them gets their GitHub tokens, npm credentials, and cloud API keys stolen. Their machine becomes a SOCKS5 proxy node. Their own repositories and packages become distribution vectors for the next wave of infections. And they receive a fileless PE loader that executes arbitrary malware inside their Node.js process, below the detection threshold of most endpoint security products.

The technical sophistication is real -- Rust, Neon N-API bindings, direct NT API syscalls, blockchain C2, Google Calendar covert channels -- but the most dangerous aspect is the worm propagation model. This is a supply chain attack that builds its own supply chain. Every compromised developer extends the blast radius.

If you are a defender:

  • Check every developer workstation in your organization against the extension list above. Today.
  • Rotate all GitHub tokens, npm tokens, and cloud credentials on any machine where an infected extension was installed.
  • Audit your npm packages for unexpected .node files containing PE headers.
  • Deploy the YARA rule above to hunt for the loader in your environment.
  • Report the nine live extensions to Microsoft via the VS Code Marketplace "Report Abuse" mechanism.

If you are Microsoft:

  • These extensions were flagged by community researchers in October 2025. It is now March 2026. Nine of twelve remain live. The marketplace review process has a gap, and developers are paying for it.

If you are the threat actor:

  • You left your build paths in the binary. You left the PDB name. You left the compile timestamp. You left the crates.io registry hash. We are clustering everything you have ever built from that Cargo environment. The investigation is not over.

Timeline

DateEvent
October 2025GlassWorm campaign begins; 12+ VS Code extensions compromised
2025-10-27Community detection by Sebastian Broers / ConbroIT Security (Munich)
2025-11-19PE loader compiled (this sample): generate.dll built by Administrator
2025-12-08Community scanner glassworm-scanner published
2026-03-16Sample submitted to threat intelligence platform
2026-03-16Breakglass analysis confirms 9/12 extensions still live on marketplace

References

  • GitHub: natorus87/vscode-extension-cleanup-glassworm -- Extension list, cleanup scripts, campaign documentation
  • GitHub: cahyod/glassworm-scanner -- Detection signatures, C2 pattern identification
  • ConbroIT Security (Munich, Germany) -- Initial community response and incident handling
  • Rust crate memexec 0.2.0 -- Reflective PE loader library (legitimate crate, abused by actor)
  • Rust crate neon 1.0.0 -- Node.js native addon framework (legitimate crate, abused by actor)

Analysis by GHOST -- Breakglass Intelligence Published: 2026-03-16 TLP:WHITE -- Unrestricted distribution

Share