Attackers phished a well-known npm maintainer (Qix-). With that access, they pushed malicious updates to 18+ extremely common packages—including chalk
, debug
, ansi-styles
, strip-ansi
, wrap-ansi
, color-convert
, and others—collectively accounting for ~2B downloads per week. The payload is a browser-side interceptor: it hooks fetch
, XMLHttpRequest
, and wallet APIs (e.g., window.ethereum
) to silently rewrite crypto addresses/approvals so funds or approvals go to attacker-controlled wallets. CLI usage wouldn’t normally trigger it; browser-embedded usage can.
Bottom line: if you built and shipped a web app from September 8 onward with one of the listed bad versions, assume risk and rotate secrets where appropriate.
Confirmed malicious packages & versions
Independent teams converged on the same set (some reports say 18, others “20+” as monitoring widened). The versions below are the malicious releases you must root out:
ansi-styles@6.2.2
debug@4.4.2
chalk@5.6.1
supports-color@10.2.1
strip-ansi@7.1.1
ansi-regex@6.2.1
wrap-ansi@9.0.1
color-convert@3.1.1
color-name@2.0.1
is-arrayish@0.3.3
slice-ansi@7.1.1
color-string@2.1.1
simple-swizzle@0.2.3
supports-hyperlinks@4.1.1
has-ansi@6.0.1
chalk-template@1.1.1
backslash@0.2.1
(Sources list the same set with minor ordering; some also mention color@5.0.1
.)
Phishing domain used: npmjs.help
(registered Sept 5, 2025).
How the malware works
- Hooks core browser APIs: wraps
fetch
,XMLHttpRequest
, and common wallet interfaces (window.ethereum
, Solana methods) to inspect/alter requests & responses. - Pattern matches addresses: detects ETH, BTC (legacy/SegWit), SOL, TRON, LTC, BCH addresses.
- Rewrites destinations/approvals: swaps legitimate targets with attacker addresses; can rewrite
eth_sendTransaction
, ERC-20 approvals (0x095ea7b3
), transfers (0xa9059cbb
), etc. - UI can still look “right” while the signed payload is poisoned.
This is not your usual “exfil secrets” loader. It’s a transaction manipulator aimed at web3 flows in the browser. If you only use these libs in Node/CLI context, the implant likely never executed—but if your build piped compromised code into any browser bundle, you have exposure.
Am I affected? Quick triage
- Search your lockfiles (
package-lock.json
,pnpm-lock.yaml
,yarn.lock
) for the exact bad versions above. If any match, you were exposed at build time. - Check build timestamps: any production build between Sept 8–9, 2025 that resolved those versions should be treated as suspect.
- Look for browser usage: if affected packages landed in client bundles (not just dev/CLI), treat as high risk.
Immediate remediation (do this now)
- Blow away caches and reinstall from scratch
rm -rf node_modules npm cache clean --force npm ci # or: pnpm install --frozen-lockfile / yarn install --frozen-lockfile
Ensure your lock file resolves to previous known-good or fixed versions. (Many maintainers and registries have already pulled the trojanized tags.) - Pin and verify
- Keep lockfiles checked in.
- Use
npm ci
/--frozen-lockfile
. - Consider temporary resolutions/overrides to force safe versions.
- Purge and redeploy
- Rebuild all affected projects; purge CDN/build caches so no old bundles linger. (Vercel did this platform-wide; follow suit.)
- Production monitoring
- If you handle web3 in the browser, add runtime checks to block monkey-patched
window.ethereum
(e.g., verify provider methods’ source or seal the provider early). - Watch for anomalous wallet interactions and unexpected destination addresses.
- If you handle web3 in the browser, add runtime checks to block monkey-patched
- Incident hygiene
- If any real funds moved during the window, treat as compromise: notify users, rotate relevant API keys/secrets, and document timelines for auditors.
Hardening for next time (yes, there’ll be a next time)
- 2FA + phishing-resistant auth for registry accounts (FIDO2 keys, not SMS/TOTP where possible).
- Scoped, short-lived tokens for CI; keep publish tokens offline or behind approval gates.
- Proactive allow/deny: use
npm config set ignore-scripts true
in CI for builds that don’t need install scripts; use tools that pre-scan packages before install. (Multiple vendors released emergency rules/detections within hours.) - SBOM + diff checks: generate SBOMs on every build and alert on unexpected dependency/version drift.
- Bundle boundary checks: add unit/e2e tests that fail builds if
window.fetch
,XMLHttpRequest
, or wallet APIs are wrapped unexpectedly in production bundles.
Why this incident was scary (and why it wasn’t worse)
The attacker chose ubiquitous, boring utilities—color parsing, ANSI helpers, small debug wrappers. That guarantees reach. But the payload focused narrowly on browser crypto flows, not API-key theft or generic RCE. If the same blast radius had targeted secrets, it could’ve been a bloodbath. We got lucky. Don’t plan on luck next time.
References & live trackers
- Aikido’s original write-up (IOC list, code details, timestamps).
- Semgrep advisory & detection rule notes.
- JFrog analysis (payload structure, obfuscation).
- Vercel incident response (platform-level mitigations).
Appendix: quick grep helpers
# Find any malicious versions in lockfiles
grep -E 'ansi-styles@6.2.2|debug@4.4.2|chalk@5.6.1|supports-color@10.2.1|strip-ansi@7.1.1|ansi-regex@6.2.1|wrap-ansi@9.0.1|color-convert@3.1.1|color-name@2.0.1|is-arrayish@0.3.3|slice-ansi@7.1.1|color-string@2.1.1|simple-swizzle@0.2.3|supports-hyperlinks@4.1.1|has-ansi@6.0.1|chalk-template@1.1.1|backslash@0.2.1' \
package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null
# For monorepos:
find . -name "package-lock.json" -o -name "pnpm-lock.yaml" -o -name "yarn.lock" \
-exec grep -HnE 'debug@4.4.2|chalk@5.6.1' {} \;
Leave a Comment