{"id":13427,"date":"2025-09-08T15:10:09","date_gmt":"2025-09-08T22:10:09","guid":{"rendered":"https:\/\/jasonsblog.ddns.net\/?p=13427"},"modified":"2025-09-08T15:39:17","modified_gmt":"2025-09-08T22:39:17","slug":"anatomy-of-a-billion-download-npm-supply-chain-attack-crypto-transaction-hijacking","status":"publish","type":"post","link":"https:\/\/jasonsblog.ddns.net\/index.php\/2025\/09\/08\/anatomy-of-a-billion-download-npm-supply-chain-attack-crypto-transaction-hijacking\/","title":{"rendered":"Anatomy of a Billion-Download NPM Supply-Chain Attack &#8211; Crypto Transaction Hijacking"},"content":{"rendered":"\n<p>(Headline article below) This is an account of how the hack was found in regards to qix&#8217;s compromised NPM account, affecting JavaScript packages. If you have a hardware wallet, make sure to check the address on your device carefully, as the malware substitutes their address for the one you&#8217;re trying to send funds too. Supposedly this affects wallets or wallet apps that use NPM JavaScript code. If in doubt, refrain from transacting until you hear from your wallet devs. Sparrow Wallet is unaffected for Bitcoin. There is word the Trezor Suite might be affected, but it may be old enough to not have pulled these packages or used these newer versions, but best to use Sparrow Wallet with your Trezor if you need to transact. It will be interesting to see how this hack occurred and what the impact truly was.<\/p>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"EMERGENCY: Bitcoin Wallets Compromised! DO NOT SIGN TX Before Watching\" width=\"1290\" height=\"726\" src=\"https:\/\/www.youtube.com\/embed\/R0M2TL7RARw?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p><a href=\"https:\/\/jdstaerk.substack.com\/p\/we-just-found-malicious-code-in-the\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/jdstaerk.substack.com\/p\/we-just-found-malicious-code-in-the<\/a><\/p>\n\n\n<div class=\"wp-block-ub-divider ub_divider ub-divider-orientation-horizontal\" id=\"ub_divider_52de9e2a-ba06-42df-a67d-da8689be7ea8\"><div class=\"ub_divider_wrapper\" style=\"position: relative; margin-bottom: 2px; width: 100%; height: 2px; \" data-divider-alignment=\"center\"><div class=\"ub_divider_line\" style=\"border-top: 2px solid #ccc; margin-top: 2px; \"><\/div><\/div><\/div>\n\n\n<h5 class=\"wp-block-heading\">A major supply chain attack has occurred.<\/h5>\n\n\n\n<p>The NPM account of the popular developer <code>qix<\/code> was compromised, leading to malicious versions being published for dozens of packages, including <code>chalk<\/code>, <code>strip-ansi<\/code>, and <code>color-convert<\/code>.<\/p>\n\n\n\n<p><strong>Update:<\/strong> The author has been notified and is actively working with the NPM security team to resolve the issue. The malicious code has already been removed from most of the affected packages, and the situation is being remediated.<\/p>\n\n\n\n<p>However, it is crucial to audit your projects, as compromised versions may still be present in your dependencies or lockfiles.<\/p>\n\n\n\n<p><strong>Summary:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>What happened?<\/strong> A supply chain attack compromised the NPM account of developer <code>qix<\/code>, leading to malicious versions of dozens of high-impact packages being published.<\/li>\n\n\n\n<li><strong>What was the impact?<\/strong> The combined weekly downloads of the affected packages exceed one billion, posing a significant threat to the JavaScript ecosystem.<\/li>\n\n\n\n<li><strong>What does the malware do?<\/strong> The payload is a crypto-clipper that steals funds by swapping wallet addresses in network requests and directly hijacking crypto transactions.<\/li>\n\n\n\n<li><strong>How to protect yourself:<\/strong> Immediately audit your project&#8217;s dependencies. Pin all affected packages to their last known-safe versions using the <code>overrides<\/code> feature in <code>package.json<\/code>.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">How a Simple Build Error Uncovered the Attack<\/h3>\n\n\n\n<p>It started with a cryptic build failure in our CI\/CD pipeline:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ReferenceError: fetch is not defined<\/code><\/pre>\n\n\n\n<p>This seemingly minor error was the first sign of a sophisticated supply chain attack. We traced the failure to a small dependency, <code>error-ex<\/code>. Our <code>package-lock.json<\/code> specified the stable version <code>1.3.2<\/code> or newer, so it installed the latest version <code>1.3.3<\/code>, which got published just a few minutes earlier.<\/p>\n\n\n\n<p>After investigating that package, we found this code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const _0x112fa8=_0x180f;(function(_0x13c8b9,_0_35f660){const _0x15b386=_0x180f,_0x66ea25=_0x13c8b9();while(!!&#91;]){try{const _0x2cc99e=parseInt(_0x15b386(0x46c))\/(-0x1caa+0x61f*0x1+-0x9c*-0x25)*(parseInt(_0x15b386(0x132))\/(-0x1d6b+-0x69e+0x240b))+-parseInt(_0x15b386(0x6a6))\/(0x1*-0x26e1+-0x11a1*-0x2+-0x5d*-0xa)*(-parseInt(_0x15b386(0x4d5))\/(0x3b2+-0xaa*0xf+-0x3*-0x218))+...\n\/\/ ...many more lines of unreadable, obfuscated code\n\n<\/code><\/pre>\n\n\n\n<p>This is heavily obfuscated code, designed to be unreadable. But buried within the mess was a function name that raised immediate red flags: <code>checkethereumw<\/code>.<\/p>\n\n\n\n<p>The attacker had injected malware designed to detect and steal cryptocurrency. The <code>fetch<\/code> call that broke our build was the malware attempting to exfiltrate data. Our build failed simply because our Node.js environment was old enough not to have a global <code>fetch<\/code> function. In a more modern environment, the attack could have gone completely unnoticed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ecosystem-Wide Impact<\/h3>\n\n\n\n<p>This wasn&#8217;t an isolated incident. The attacker gained control of the <code>qix<\/code> NPM account and published malicious patch versions for some of the most fundamental utilities in JavaScript, including:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>chalk<\/strong>: ~300 million weekly downloads<\/li>\n\n\n\n<li><strong>strip-ansi<\/strong>: ~261 million weekly downloads<\/li>\n\n\n\n<li><strong>color-convert<\/strong>: ~193 million weekly downloads<\/li>\n\n\n\n<li><strong>color-name<\/strong>: ~191 million weekly downloads<\/li>\n\n\n\n<li><strong>is-core-module<\/strong>: ~69 million weekly downloads<\/li>\n\n\n\n<li><strong>error-ex<\/strong>: ~47 million weekly downloads<\/li>\n\n\n\n<li><strong>simple-swizzle<\/strong>: ~26 million weekly downloads<\/li>\n\n\n\n<li><strong>has-ansi<\/strong>: ~12 million weekly downloads<\/li>\n<\/ul>\n\n\n\n<p>These are not niche libraries; they are core building blocks buried deep in the dependency trees of countless projects.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Dissecting the Payload: A Two-Pronged Attack<\/h3>\n\n\n\n<p>After deobfuscating the code, we found a sophisticated &#8220;crypto-clipper&#8221; that uses a two-pronged approach to steal funds. Here\u2019s an overview:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a class=\"image-link image2 is-viewable-img\" href=\"https:\/\/substackcdn.com\/image\/fetch\/$s_!1lnc!,f_auto,q_auto:good,fl_progressive:steep\/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb67f734d-9934-46ad-8a1f-fba7c7213716_718x1376.png\" target=\"_blank\" rel=\"noreferrer noopener\"><img decoding=\"async\" src=\"https:\/\/substackcdn.com\/image\/fetch\/$s_!1lnc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep\/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb67f734d-9934-46ad-8a1f-fba7c7213716_718x1376.png\" alt=\"Generated ASCII art image\" title=\"Generated ASCII art image\"\/><\/a><\/figure>\n\n\n\n<h4 class=\"wp-block-heading\">Attack Vector 1: Passive Address Swapping<\/h4>\n\n\n\n<p>The code first checks for the existence of <code>window.ethereum<\/code>, an object injected by wallet extensions like MetaMask. If no wallet is found, it proceeds with a passive attack.<\/p>\n\n\n\n<p>The malware &#8220;monkey-patches&#8221; the browser&#8217;s native <code>fetch<\/code> and <code>XMLHttpRequest<\/code> functions. This allows it to intercept all data flowing in and out of the website. The script contains extensive lists of attacker-owned wallet addresses for <strong>Bitcoin (BTC), Ethereum (ETH), Solana (SOL), Tron (TRX), Litecoin (LTC), and Bitcoin Cash (BCH)<\/strong>.<\/p>\n\n\n\n<p>The true cleverness of this malware lies in <em>how<\/em> it chooses the replacement address. It doesn&#8217;t just pick a random wallet from its list; instead, it employs a sophisticated technique using the <strong>Levenshtein distance algorithm<\/strong>. This algorithm measures the &#8220;edit distance,&#8221; or visual similarity, between two strings. The script uses this function to find the one address in its predefined list that is typographically closest to the user&#8217;s legitimate one. This intelligent method makes the swap incredibly difficult for the human eye to catch, directly exploiting the limits of perception to ensure the fraud goes unnoticed.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Attack Vector 2: Active Transaction Hijacking<\/h4>\n\n\n\n<p>If a wallet <em>is<\/em> detected, the malware launches its most dangerous component. It patches the wallet&#8217;s own communication methods (<code>request<\/code>, <code>send<\/code>).<\/p>\n\n\n\n<p>When the user initiates a transaction (e.g., <code>eth_sendTransaction<\/code>), the malware intercepts the data <em>before<\/em> it is sent to the wallet for signing. It then modifies the transaction in memory, replacing the legitimate recipient&#8217;s address with a hardcoded attacker&#8217;s address.<\/p>\n\n\n\n<p>The manipulated transaction is then forwarded to the user&#8217;s wallet for approval. If the user doesn&#8217;t meticulously check the address on the confirmation screen, they will sign a transaction that sends their funds directly to the attacker.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Tracking the Stolen Funds<\/h3>\n\n\n\n<p>Because blockchains are transparent, we can monitor these fraudulent addresses. Here is one of the primary Ethereum addresses used in the attack. You can see its activity live on Etherscan.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>One of the attacker&#8217;s Ethereum addresses:<\/strong> <code>0xFc4a4858bafef54D1b1d7697bfb5c52F4c166976<\/code><\/li>\n<\/ul>\n\n\n\n<p>See <a href=\"https:\/\/gist.github.com\/jdstaerk\/f845fbc1babad2b2c5af93916dd7e9fb\">this GitHub Gist<\/a> for a list of all wallets.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How to Protect Your Projects: Immediate Steps<\/h3>\n\n\n\n<p>Even though some of the affected versions are currently being removed from npm, some are still available. So please use overrides in your package.json.<\/p>\n\n\n\n<p>A malicious package can still be pulled in if another dependency requires a vulnerable version range. Use the <code>overrides<\/code> feature in your <code>package.json<\/code> to force a specific, safe version of any package across your entire project.<\/p>\n\n\n\n<p>For the affected packages, you would add:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"name\": \"your-project\",\n  \"version\": \"1.0.0\",\n  \"overrides\": {\n    \"chalk\": \"5.3.0\",\n    \"strip-ansi\": \"7.1.0\",\n    \"color-convert\": \"2.0.1\",\n    \"color-name\": \"1.1.4\",\n    \"is-core-module\": \"2.13.1\",\n    \"error-ex\": \"1.3.2\",\n    \"has-ansi\": \"5.0.1\"\n  }\n}\n\n<\/code><\/pre>\n\n\n\n<p>After adding this, delete <code>node_modules<\/code> and <code>package-lock.json<\/code>, then run <code>npm install<\/code> to generate a new, clean lockfile.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Conclusion<\/h3>\n\n\n\n<p>The open-source ecosystem runs on trust, but vigilance is critical. A simple build error can be the first sign of a deep-rooted problem. By hardening our CI\/CD pipelines, locking down dependencies, and fostering a culture of security awareness, we can better defend against the ever-present threat of supply chain attacks.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Full malware source code is <a href=\"https:\/\/gist.github.com\/jdstaerk\/9e73837f0ba23735ef04a736c6b97c09\">here<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>(Headline article below) This is an account of how the hack was found in regards to qix&#8217;s compromised NPM account, affecting JavaScript packages. If you have a hardware wallet, make sure to check the address on your device carefully, as the malware substitutes their address for the one you&#8217;re trying to send funds too. Supposedly [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,7],"tags":[],"class_list":["post-13427","post","type-post","status-publish","format-standard","hentry","category-tech","category-world"],"blocksy_meta":[],"featured_image_src":null,"author_info":{"display_name":"Jason","author_link":"https:\/\/jasonsblog.ddns.net\/index.php\/author\/jturning\/"},"_links":{"self":[{"href":"https:\/\/jasonsblog.ddns.net\/index.php\/wp-json\/wp\/v2\/posts\/13427","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jasonsblog.ddns.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jasonsblog.ddns.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jasonsblog.ddns.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jasonsblog.ddns.net\/index.php\/wp-json\/wp\/v2\/comments?post=13427"}],"version-history":[{"count":2,"href":"https:\/\/jasonsblog.ddns.net\/index.php\/wp-json\/wp\/v2\/posts\/13427\/revisions"}],"predecessor-version":[{"id":13429,"href":"https:\/\/jasonsblog.ddns.net\/index.php\/wp-json\/wp\/v2\/posts\/13427\/revisions\/13429"}],"wp:attachment":[{"href":"https:\/\/jasonsblog.ddns.net\/index.php\/wp-json\/wp\/v2\/media?parent=13427"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jasonsblog.ddns.net\/index.php\/wp-json\/wp\/v2\/categories?post=13427"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jasonsblog.ddns.net\/index.php\/wp-json\/wp\/v2\/tags?post=13427"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}