skip to content

A Near-Miss: How the NPM Breach Almost Wreaked Havoc for Crypto Users

NPM Breach

The node package manager (NPM) account for a reputable software developer who goes by the pseudonym qix was compromised Monday, sending shockwaves through the Javascript community and the web at large, not least of all cryptocurrency users. qix is a widely respected maintainer of many top JavaScript packages that together receive more than 1 billion downloads each week. Before we diagnose what happened and how investors can protect their digital assets, we will explain the mechanics behind the incident and why it had the potential to be widespread. Fortunately, our research indicates the exploit itself was not especially fruitful, which we will explain in more detail below.

Much of modern business runs online, with a growing share onchain. At the core of all of these tools and services are the millions of lines of code written by software developers. Developers at large companies, Web3 startups, and solo projects all follow a similar path. They store code on GitHub, package it for internal or public use, and publish it through a package manager. The main package manager for JavaScript programming language is the NPM, which was at the core of this compromise. While Ethereum smart contracts are written mostly in the Solidity language, the surrounding stack relies heavily on JavaScript and TypeScript. Front ends, bots, scripts, and Web3 libraries all depend on Javascript and NPM, so a poisoned package can spread through core repositories used by teams large and small. Web3, as well as most of the internet, is built on JavaScript.

Although many parts of a company codebase are written from scratch, teams also import packages to save time and build on trusted work. Developers rely on NPM to bring packages into their codebase and then manage those packages with build files and deployment pipelines. That choice is a double-edged sword; on the one hand, you may save development time and strengthen your code by using reputable software packages. On the other hand, you may expose yourself to malicious packages. That is exactly what happened in this incident.

What happened?

On Monday, Sept. 8, qix received a phishing email for his NPM account, which allowed attackers to publish malicious versions of key JavaScript libraries to the NPM registry.

Some of these libraries included:

  • chalk: ~300 million weekly downloads

  • strip-ansi: ~261 million weekly downloads

  • color-convert: ~193 million weekly downloads

  • color-name: ~191 million weekly downloads

  • error-ex: ~47 million weekly downloads

  • simple-swizzle: ~26 million weekly downloads

  • has-ansi: ~12 million weekly downloads

Developers who had these or related NPM packages anywhere in their dependency tree (the software’s supply chain) could have pulled tainted updates without realizing it. Even if they did not install the packages directly, the malware could have arrived as a dependency of a dependency. Several pathways make this possible, and there are standard controls that help organizations reduce the risk.

As noted earlier, developers rely on JavaScript libraries distributed through NPM to save time and improve reliability. Projects declare dependencies (necessary software packages) in a package.json file and often include a lockfile such as package_lock.json to identify exact versions. In short, these files specify exactly which packages and versions the build should install. When versions are locked and the lockfile is respected, builds do not pick up a malicious release. The risk surfaces when version ranges are not defined, or when lockfiles are missing, ignored, or regenerated. Some developers also deploy the latest release by specifying a version range that automatically pulls newer stable versions. Any build that ran while the malicious version was live could have pulled in the malicious package version. Automated builds or continuous integration (CI) systems that resolve dependencies on each run can widen that exposure.

How was this discovered and who was affected?

Charles Guillemet, chief technology officer at crypto hardware wallet maker Ledger, was the first to publicize this exploit on X (formerly Twitter), garnering over 8 million views. His tweet cited a very in-depth report by a software engineer named JD Stärk. Several reputable wallets and DeFi apps reported no impact from the incident. Ledger, Phantom, and MetaMask said their platforms were safe, with MetaMask citing version pinning, strict release checks, and the use of other threat detection mechanisms. Uniswap said none of its apps are at risk. Aerodrome, Blast, Blockstream Jade, and Revoke.cash also reported no impact.

Stärk said a colleague encountered a cryptic build error during a rebuild that traced to the error-ex package:

ReferenceError: Fetch is not defined

The colleague’s package-lock.json allowed error-ex version 1.3.2 or newer, so the build pulled the malicious 1.3.3 release. It was caught because their Node.js environment did not provide a global fetch function. Fetch is a standard API that makes network requests and returns the response. Because the malware assumed fetch existed, the call failed during testing and exposed the issue. In newer environments where fetch is available by default, the code could have run unnoticed and been deployed.

Unlike the environment Stärk described, where the missing global fetch caused an obvious failure, Ledger may have been building against a newer runtime that provides fetch by default. In that case the payload could run without error. Detection would then rely on automated safeguards, such as scanners that flag unexpected network calls, checks that verify package integrity and signatures, and pipeline rules that block unknown domains. However, in this exploit the packages were published through legitimate NPM workflows and did not contact suspicious domains, so domain verification and network call checks would not have caught it.

Ledger may have surfaced the issue during testing or automated deployment checks. Dependency differentials might have shown unexpected additions, static analysis could flag network calls to unfamiliar domains, and integrity checks on the bundled artifacts can highlight suspicious code paths. A staging run or a small canary deploy might also trigger telemetry that records unusual fetch activity. In any of these scenarios, the likely response is to freeze versions, roll back the artifact, and audit the dependency tree while verifying that production traffic was not affected.

Ledger initially described this as a large-scale supply chain attack, but follow-up analysis shows the observed impact was limited.

Dissecting the Attack

The attacker set up the exploit by creating 280 wallets across six blockchains: Bitcoin (BTC), Ethereum (ETH), Solana (SOL), Tron (TRX), Litecoin (LTC), and Bitcoin Cash (BCH). The exploit used two different methods of attack to replace a sender’s wallet address with one of the attacker’s addresses.

Attack Vector 1: Passive Address Swapping

The code first checks whether a wallet extension such as MetaMask or Phantom is active in your browser. If none is found, it begins intercepting traffic to and from the site you are using. It then confirms you are on a crypto-native site initiating a transfer and swaps the recipient’s address with a near match from its list. The exploit relies on the user not noticing the small difference before hitting “send.” This lookalike approach makes it hard to spot the few characters that differ from the address you pasted or typed.

Attack Vector 2: Active Transaction Hijacking

If a wallet extension is present, the malware switches to its most dangerous method. When you initiate a transaction, it intercepts the data before it reaches the extension, replaces the recipient address in memory with a lookalike controlled by the attacker, and then forwards the altered request to your wallet for confirmation. Because the address is a near match, the few character differences are easy to miss. If you do not verify the address character by character, you can end up sending funds to the attacker.

When the user signs a transaction in a wallet, it will display the recipient, asset, amount, and fee for review before broadcasting to the network. However, this attack swaps the recipient in memory before the prompt appears, so the wallet shows the attacker’s address as if it were correct. To spot the ruse, the user would have to check the address manually or by using an allow list. This feature will either block any transaction to an address that is not specified on the list or will display a blatant warning to the user before sending it. Blind signing is when an application doesn’t prompt a user to confirm their transaction, so the address swap is almost impossible to detect, which makes this mode the riskiest and most likely to send funds to the attacker.

Impact of the Attack

Arkham’s research indicates the attack was far less widespread than it could have been. In total, they identified 17 transactions and $1,043.21 in stolen funds, including $436.84 that appears to have been intentionally sent to the attacker as a taunt.

Galaxy Digital was not affected by this exploit. In general, institutions and their trading desks experienced less impact than everyday users because they interact more directly with DeFi protocols (i.e., not via their front-ends), and use controls such as address allow lists, multi-step approvals, and disabled blind signing among other measures.

The NPM community moved quickly to patch the affected packages, which limited the impact on developers. Maintainers published clean versions, issued advisories, and prompted projects to upgrade, shortening the window during which the malicious code could run.

Crypto ecosystem tools reacted fast as well. Block explorers such as Etherscan and Solscan flagged the attacker addresses, added warning labels on address and transaction pages, and pushed those tags through their APIs. This made the addresses easier to spot, discouraged reuse, and helped wallets and analytics services deprioritize or block them.

Etherscan NPM Hack

As of Sept. 11

Defending Against This Attack: Actions for Developers and Users

Developers: Upgrade to the fixed releases and pin them so transitive installs cannot pull vulnerable versions. Some affected versions may still be reachable, so enforce safe versions across your project and regenerate a clean lockfile. Keep the lockfile in source control, enable automated update alerts, and fail CI if a vulnerable version appears. Remove unused dependencies, review new maintainers and permission changes, and make sure your build and install process always yields the same result by pinning versions and committing lockfiles.

DeFi users: Do not use blind signing. Keep it disabled except when absolutely required. For normal signed transactions, review the full recipient address before you approve. Use an address book or allow list, compare more than the first and last characters, and send a small test when paying a new recipient. Keep extensions up to date, remove ones you do not use, and access apps through saved bookmarks rather than links in messages or ads.

Conclusion

This was a near-miss. The attackers briefly had a path into millions of apps through widely used packages but did not push broad code execution. Instead, they relied on an end user address swap that was overt, quickly detected, and contained within hours. The industry should treat that outcome as luck, not strategy.

A more capable actor could have used the same foothold for silent supply chain tampering or remote code execution with far greater impact. With code running in the web page, a sophisticated attacker could monitor and modify traffic across all websites and Web3 apps; harvest credentials, session cookies, and API keys; inject skimmers; and quietly exfiltrate data.

The takeaway is simple. Keep dependencies current and builds locked down, and coach users to avoid blind signing and to verify recipient addresses on every send.

You are leaving Galaxy.com

You are leaving the Galaxy website and being directed to an external third-party website that we think might be of interest to you. Third-party websites are not under the control of Galaxy, and Galaxy is not responsible for the accuracy or completeness of the contents or the proper operation of any linked site. Please note the security and privacy policies on third-party websites differ from Galaxy policies, please read third-party privacy and security policies closely. If you do not wish to continue to the third-party site, click “Cancel”. The inclusion of any linked website does not imply Galaxy’s endorsement or adoption of the statements therein and is only provided for your convenience.