Malicious code was embedded in specialized packages for developers that are used to create global Internet infrastructure

Overview of the largest NPM attack in history: what it is and how it relates to crypto

10.09.2025

286

5 min

On September 8, the largest attack in history on the NPM infrastructure targeting cryptocurrency users took place. It could have affected billions of users around the world, but the developer community detected the threat in time. GetBlock AML Research explains in simple terms what NPM is and why attackers choose this tool for hacking.

What is NPM

NPM is the world’s largest catalog (library) of ready-made software “parts” for developers. Imagine a store with millions of small parts that programmers use to assemble their applications. Instead of writing everything from scratch each time, they take these ready-made “building blocks” and quickly build programs and services.

These “parts” are called packages. Any developer can create them and make them publicly available. Other programmers use these packages in their projects — for example, in websites, mobile applications, or even in programs related to cryptocurrencies.

And this is where the connection with cryptocurrencies comes in. Most modern crypto wallets, exchanges, and other services are written in programming languages that actively use NPM packages. If an attacker injects malicious code into one of these packages, all programs that use this “part” will also be infected.

In the case of the recent attack, hackers embedded code that replaced cryptocurrency addresses. That is, when a person tried to send money, instead of their real address, someone else’s address was secretly substituted — the attacker’s wallet.

How a simple build error helped detect an attack

It all started with a strange error during automatic build in the CI/CD pipeline, which was noticed by one of the developers:

ReferenceError: fetch is not defined

This seemingly minor error turned out to be the first sign of a complex attack. The error was traced to its source in a small dependency called error-ex. Further investigation revealed that malicious code had been injected into the package. It contained a reference to a suspicious function called checkethereumw, which immediately raised concerns among the developers.

The attacker embedded malware designed to detect and steal cryptocurrency. The fetch error occurred because the Node.js environment was quite old and did not have this function. In newer systems, the attack could have gone completely unnoticed.

Scale of impact on the ecosystem

After gaining control of the qix developer account, the attacker published malicious updates for a number of fundamental JavaScript utilities, including:

  • chalk: ~300 million downloads per week
  • strip-ansi: ~261 million downloads per week
  • color-convert: ~193 million downloads per week
  • color-name: ~191 million downloads per week
  • error-ex: ~47 million downloads per week
  • simple-swizzle: ~26 million downloads per week
  • has-ansi: ~12 million downloads per week

These are not niche libraries, but essential “building blocks” deeply embedded in the dependencies of many projects.

Malicious code analysis: a two-stage attack

Inside the infected packages was a sophisticated crypto clipper that used a two-stage scheme to steal funds.

Attack vector 1: Passive address spoofing

The code first checked for the presence of the window.ethereum object, which is added by browser wallets such as MetaMask. If the wallet was not detected, “passive” mode was activated.

The malicious code replaced the browser’s built-in functions for network requests in order to intercept all incoming and outgoing data. Inside were lists of the hacker’s wallets for Bitcoin (BTC), Ethereum (ETH), Solana (SOL), Tron (TRX), Litecoin (LTC), and Bitcoin Cash (BCH).

What made it particularly sophisticated was that the program did not simply substitute a random address from the list, but chose one that was as similar as possible to the real one. To do this, it used the Levenshtein algorithm, which measures the “similarity of strings.” Thanks to this, the substitution looked so plausible that the victim might not notice it.

Attack vector 2: Active interference in transactions

If the wallet was detected, the second, more dangerous part of the code was launched. It intercepted the wallet’s functions (for example, sendTransaction).

When the user attempted to perform an operation, the code silently replaced the recipient’s address with a predetermined address belonging to the attackers. The transaction was then sent for confirmation. If the user did not check the address manually, they themselves signed off on the transfer of funds to the scammers.

Tracking stolen funds

Despite the scale of the attack and billions of downloads of malicious files, the attacker received only 0,1 ETH (about $430 at the time of publication), which is stored at the address:

0xFc4a4858bafef54D1b1d7697bfb5c52F4c166976

Interesting fact: 0,1 ETH was received from a single user with the domain name leonard.eth (0xFD89d3831C6973fB5BA0b82022142b54AD9e8d46).

Visualization: MistTrack

The full list of the attacker’s wallets is published on GitHub

How to protect your projects: urgent measures for developers

Although some of the infected versions have already been removed from NPM, some may still be available. Therefore:

  • use the overrides function in package.json;
  • this will force secure versions of packages to be fixed throughout the project.

After making changes, you must delete the node_modules folder and the package-lock.json file, then re-run the dependencies’ installation (npm install) to generate a clean lock file.

Subscribe to Getblock Magazine and stay up to date with the latest news from the world of cryptocurrencies and the digital economy