Back to Research
Analysis of the NPM Supply Chain Attack
2025-09-10
incident-analysis

Analysis of the NPM Supply Chain Attack

A technical breakdown of the September 2025 malware campaign that targeted 2 billion downloads by compromising 'chalk' and 'debug' packages.

View Malware Samples

⚠️ WARNING: MALICIOUS CODE AHEAD The code analyzed below is from a live malware campaign. DO NOT EXECUTE unless in a secure, sandboxed environment.

Executive Summary

On September 8, 2025, a massive supply chain attack compromised the account of a prolific maintainer, qix. This led to the publication of malicious versions of 18 popular packages, including chalk and debug, which are collectively downloaded over 2 billion times per week.

The payload was a sophisticated, client-side malware designed to steal cryptocurrency from the users of websites that bundled these compromised packages.

This analysis is a companion to my detailed article: How One Phishing Email Injected Crypto-Stealing Malware into Billions of NPM Downloads.


Attack Vector

The attack was initiated through a well-executed social engineering campaign:

  1. The Phish: The maintainer received an email from support@npmjs.help claiming their 2FA needed updating.
  2. Credential Hijacking: A cloned login page captured the username, password, and the Time-based OTP (2FA) in real-time.
  3. Deployment: Using the hijacked session, the attackers published patch versions containing a single, obfuscated line of JavaScript injected into the package's core file.

Malware Analysis

The malware utilized a two-stage activation process to remain stealthy.

Stage 1: Environment Check

The script first checks if it is running in a browser with a Web3 wallet installed (window.ethereum). If not, it falls back to a passive mode to avoid detection by automated scanners.

// The malware only activates its full potential if a Web3 wallet is detected.
if (typeof window !== 'undefined' && typeof window.ethereum !== 'undefined') {
  detectAndInitializeWalletHooks();
} else {
  // If no wallet is found, it falls back to only the network interception module.
  initializeAddressSwapper();
}

Stage 2: The Two-Pronged Attack

Module A: Passive Network Interception

This module hijacks the browser's native fetch and XMLHttpRequest functions.

  1. It intercepts network response bodies.
  2. It uses Regex to find strings matching crypto addresses (BTC, ETH, SOL).
  3. It uses a Levenshtein distance algorithm to swap the address with a visually similar one from a hardcoded list of 280+ attacker wallets.

Module B: Active Transaction Hijacking

This module hooks into window.ethereum.request. When a user tries to sign a transaction:

  1. It inspects the transaction payload.
  2. For transfer or approve calls, it surgically replaces the to address with the attacker's wallet.
  3. The user sees a Metamask popup that looks legitimate, but signs a transaction sending funds to the attacker.

Remediation

If your project depends on chalk or debug:

  1. Verify: Run grep -r 'checkethereumw' node_modules/ to check for the malicious footprint.
  2. Reinstall: Delete node_modules and package-lock.json, then run npm install.
  3. Rotate: Assume your CI/CD environment secrets are compromised.

Indicators of Compromise (IoCs)

TypeValue
Phishing Domainnpmjs.help
C2 Domainstatic-mw-host.b-cdn.net
Attacker IP185.7.81.108
Attacker ETH0xFc4a4858bafef54D1b1d7697bfb5c52F4c166976

A full list of the 280+ hardcoded wallets is available in the deobfuscated source code.