# Active Supply Chain Attack: Malicious node-ipc Versions Published to npm

Malicious node-ipc versions 9.1.6, 9.2.3, 12.0.1 shipped an obfuscated IIFE that harvested 90+ credential categories and exfiltrated them via HTTPS and DNS TXT.

- Payout: Real-world hack
- Severity: critical
- Program: node-ipc
- Analysis by: StepSecurity
- Disclosed: 2026-05-19
- Write-up quality: 8/10
- Original: https://www.stepsecurity.io/blog/node-ipc-npm-supply-chain-attack
- Library page: https://bugbountylibrary.com/r/blog%3Astepsecurity-io-blog-node-ipc-npm-supply-chain-attack
- Tags: area:oss domain:oss impact:data-leak impact:internal-access impact:mass-exploit kind:incident platform:threat-intel sev:critical tech:nodejs technique:reverse-engineering technique:source-review vuln:dependency-confusion vuln:secrets-leak

**Target:** npm package CommonJS bundle (node-ipc.cjs)
**Root cause:** A rogue/compromised maintainer account (atiertant) published three versions with an 80KB obfuscated IIFE appended after module.exports in node-ipc.cjs, firing unconditionally on require(). No lifecycle hooks used, evading install-script scanners.
**Steps:**
1. `npm install node-ipc@12.0.1` (or 9.1.6/9.2.3) pulls tarball with 117KB node-ipc.cjs vs 37KB clean
2. On `require('node-ipc')`, IIFE at line 1271 runs; 12.0.1 first checks `SHA256(path.normalize(module.filename).toLowerCase())` against hardcoded `bf9d8c0c...940b0301`, exits silently if mismatch
3. If `__ntw` env absent, forks detached clone with `__ntw=1`, strips NODE_OPTIONS, stdio ignored
4. Daemon creates `$TMPDIR/nt-<PID>/`, runs `uname -a`, serializes env vars
5. Reads 90+ credential paths (~/.aws/credentials, ~/.ssh/id_*, ~/.kube/config, ~/.claude.json, ~/.npmrc, ~/.pgpass, .env, etc.) into ustar TAR, gzipSync
6. Exfil channel 1: HTTPS POST to `sh.azurestaticprovider.net:443` (37.16.75.69) with HMAC-SHA256 chunked body
7. Exfil channel 2: `resolver.setServers(['37.16.75.69'])` then DNS TXT queries `<hexchunk>.<session>.<idx>.<total>.bt.node.js`
**How found:** StepSecurity AI Package Analyst flagged the release; analysts noted 21-month publish gap, new maintainer with no prior history, 3.7x tarball size anomaly, and byte-identical node-ipc.cjs across three versions, then deobfuscated the custom base-16 reversed-nibble cipher to recover C2 and target list.
**Exploitation:** Attacker compromised or added maintainer account atiertant, staged one obfuscated bundle into three package.json contexts (fabricated 9.x tarballs copied from 12.x structure), published simultaneously to maximize semver blast radius. On any require() the payload daemonizes, harvests credentials, and exfiltrates via HTTPS and direct-to-C2 DNS to bypass corporate resolvers.
**Escalation:** Multi-version publish across 9.1.x/9.2.x/^9/^12/~12.0 ranges; 12.0.1 adds SHA-256 targeting gate to hit one specific victim silently while 9.x executes unconditionally; dual exfil channels for redundancy.
**Insight:** When deobfuscating custom hex-like alphabets, check nibble order — a standard decoder yielding garbage often means little-endian nibble assembly (low nibble first). Also: payloads appended after module.exports in CommonJS fire on require() without any lifecycle hook, so scan for large single-line additions at file tail, not just preinstall/postinstall scripts.
