The Great npm Compromise: A Post-Mortem
A coordinated supply chain attack on the npm ecosystem was detected today, a swift and widespread compromise that left no doubt about the increasingly concerning nature of open-source threats. While many headlines focused on a phishing email, the real story lies in the sophisticated, multi-layered payload that followed, one designed to silently exfiltrate data from crypto wallets.
This wasn’t a smash-and-grab operation, it was a highly targeted operation that leveraged social engineering to plant an advanced, client-side financial weapon. It was a perfectly executed demonstration of why a reactive, traditional security approach to application and supply chain security just isn’t enough anymore.
This new level of threat demands a new approach to security. It’s no longer enough to look for what’s wrong; we need a security model that anticipates threats by understanding the subtle, context of your code and ecosystem.
But first, the attack.
npm Supply Chain Attack: A Social Engineering Masterclass
The attack was a client-side compromise initiated by a phishing campaign against a maintainer of widely used npm packages. The attackers gained control of the maintainer’s account and used the access to inject malicious code into 19 popular dependencies, which collectively have over 2 billion weekly downloads. The injected payload was identical across all affected packages, designed to silently siphon sensitive information from systems interacting with crypto assets.
The compromised packages and their malicious versions are:
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
@5.0.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
error-ex
@1.3.3
As of the time of publication, the current state of packages is as follows:
How the Compromise Happens
There were two key ways this vulnerability could have put organizations at risk:
-
One of the compromised packages was used for the first time, and the malicious version ended up in the environment.
-
A container was rebuilt, and, during the process, one of the vulnerable packages was included. Because a file lock was not used to pin package versions, the build pulled the latest, and compromised versions from npm, which were then embedded into the artifacts.
-
If an npm update command was run, the execution would bump up the versions in the package.json to the latest (and compromised) versions.
Rebuilding the artifact at this point should retrieve the fixed package versions from npm, effectively remediating the vulnerability.
Technical Analysis of the Attack Vector and Payload
The initial compromise began with a sophisticated phishing email sent from a fraudulent domain, npmjs.help, designed to impersonate the official npm registry (npmjs.com). The email used always-reliable social engineering to trick the maintainer into “updating” their 2FA credentials on a fake login page. The credentials and token were then exfiltrated to an attacker-controlled endpoint at websocket-api2.publicvm.com.
The malicious code injected into the packages functioned as a “Web3 drainer,” or a man-in-the-browser attack, engineered to hijack cryptocurrency activity. Once active, it silently monitors for connected wallets and manipulates transactions by using techniques like transaction swapping.
The payload’s primary function was to intercept and manipulate cryptocurrency transactions by hooking into standard browser APIs like fetch and XMLHttpRequest, as well as Web3 APIs such as window.ethereum and those used by wallets like MetaMask and Phantom. Because the code tampers with the data displayed on web pages, even deposit fields or QR codes can be altered without the user’s knowledge, resulting in funds, tokens, and approvals being diverted directly to the attacker.
The malware was capable of address-swapping for a variety of cryptocurrencies, including Ethereum, Bitcoin, Solana, Tron, Litecoin, and Bitcoin Cash. It also targeted on-chain smart contract functions, such as ERC-20 token transfers and approvals, to steal a wide range of assets. The address-swapping logic was particularly advanced, using a string similarity algorithm, such as Levenshtein distance, to find a “closest looking” attacker address to the legitimate one, making the fraudulent address nearly identical to the intended one.
Conclusion
For teams without access to automated security solutions, the immediate priority is to audit projects manually. Remove or downgrade the compromised package versions, rebuild applications from clean sources, and review deployed environments for any injected code. Developers and end-users should revoke existing token approvals, rotate secrets, and, when possible, move funds to fresh wallets created offline.