DEV Community

Cover image for Anatomy of a Supply Chain Heist: The Day 'chalk' and 'debug' Became Crypto-Thieves
Figsy
Figsy

Posted on

Anatomy of a Supply Chain Heist: The Day 'chalk' and 'debug' Became Crypto-Thieves

Every day, millions of developers type a simple, almost reflexive command into their terminals: npm install. It’s the foundational act of modern web development, a gateway to a universe of open-source tools that make building complex applications possible. But this routine command, a gesture of implicit trust in a global community, can sometimes open a door to unseen threats. On September 8, 2025, this trust was weaponized in one of the most significant supply chain attacks in recent history, turning ubiquitous, "boring" packages like chalk and debug into silent crypto-thieves.

This incident was not a flaw in the code of these packages but a hijacking of the trust placed in their maintainer. A single, well-crafted phishing email set off a chain reaction that compromised packages with a combined total of over ~two billion weekly downloads, sending shockwaves through the entire JavaScript ecosystem.

The Attack: 🤔 How One Phishing Email Compromised Billions of Downloads?

The effectiveness of this supply chain attack hinged not on a complex zero-day exploit, but on the meticulous exploitation of the most vulnerable part of any security system: the human element. The attackers targeted a single, highly trusted individual to gain a foothold in an ecosystem used by millions.

🎯 The Target: A Respected Developer

The developer at the center of this incident was Josh Junon, a prolific and respected open-source contributor known by the handle qix. With over ~1,800 contributions on GitHub in the past year alone, Junon was the trusted maintainer of dozens of foundational npm packages. These were not obscure libraries; they included utilities like chalk (for terminal text styling), debug (a debugging tool), and strip-ansi (for removing ANSI escape codes), which are transitive dependencies in countless popular frameworks and applications. Combined, the affected packages accounted for a staggering ~2.6 billion weekly downloads, establishing the immense potential blast radius for any compromise.

🧲 The Lure

The attack began with a phishing email. The message was sent from the domain support@npmjs.help, a convincing lookalike of the official npmjs.com domain. This fraudulent domain npmjs.help had been registered at porkbun.com just three days before the attack, a clear sign of a premeditated and targeted operation rather than an opportunistic one.

Attached is the email Junon shared. The transparency shown here is exemplary and worth recognizing

The email's content was a masterclass in psychological manipulation, employing classic social engineering tactics to bypass the recipient's natural caution. It created a sense of urgency and fear, falsely claiming that Junon's account would be locked within 48 hours if he did not update his Two-Factor Authentication (2FA) credentials, which the email alleged were over 12 months old. According to Junon himself, the email looked "very legitimate". The attack's success was aided by circumstance; Junon was on his mobile device during a busy week, a common scenario where individuals are more likely to let their guard down. He later candidly acknowledged the compromise on platforms like HackerNews, stating, "Hi, yep I got pwned... Sorry everyone, very embarrassing".

🪄 Bypassing 2FA

Crucially, the attack was not a simple credential theft; it was designed to defeat the very 2FA protection that developers are urged to use. The phishing site was not just a static form but an active Adversary-in-the-Middle (AitM), acting as a proxy between the Junon (the victim) and the real npm service. This technique bypasses many common forms of MFA by hijacking the authenticated session itself.

The process unfolded in real-time:

  • Junon visited the fake site (npmjs.help) and entered his username and password
  • The "AitM" proxy immediately forwarded these credentials to the legitimate npmjs.com login service.
  • The real npm site responded with a challenge for a 2FA token.
  • npmjs.help (the fake site) mirrored this prompt, asking Junon for his Time-based One-Time Password (TOTP) code.
  • Junon entered the live 2FA code from his authenticator app into the fake site.
  • The proxy passed this valid, time-sensitive token to the real npm site, successfully completing the login process.
  • The attacker, sitting in the middle, intercepted the resulting session cookie. This token granted them full, authenticated access to Junon's account, rendering his password and 2FA device irrelevant for the duration of the session.

This method highlights a critical vulnerability in authentication security. While MFA methods like TOTP codes and SMS messages are effective against simple password theft, they are not phishing-resistant. An "AitM" attack doesn't break the cryptography of 2FA; it tricks the user into authenticating on the attacker's behalf. True phishing resistance requires standards like FIDO2/WebAuthn, which cryptographically bind the authentication process to the specific domain origin, making it impossible for a proxy on a different domain to relay the sign-in attempt.

Once they had control, the attackers immediately changed the email address associated with the npm account, temporarily locking Junon out and giving them a window to publish their malicious packages.


What exactly is FIDO/WebAuthn?

  • Passwordless authentication is a growing trend in IT security that seeks to replace weak passwords with more secure alternatives, enhancing both user experience and protection.
  • At its core is WebAuthn, an API created by the W3C and FIDO Alliance, which allows users to register and authenticate using public key cryptography, generating unique private-public key pairs (credentials) often associated with biometric authenticators like 'Windows Hello" or "Apple’s Touch ID".
  • The FIDO Alliance is responsible for developing technical standards for non-password authentication based on public-key cryptography and certifying interoperable solutions.
  • CTAP (Client to Authenticator Protocol), a specification from the FIDO Alliance, outlines how applications and operating systems interact with authentication devices, with CTAP2 being essential for FIDO2.
  • FIDO2 merges WebAuthn and CTAP2 to deliver strong passwordless authentication through devices capable of biometrics or security keys.
  • This approach greatly enhances security by employing unique key pairs for each application, relying on inherence (biometrics) and possession (registered device) factors instead of shared secrets, thus reducing the risks of phishing, stolen credentials, and man-in-the-middle attacks.

Rapid Detection and Response

Aikido alerted Junon via the social network Bluesky

Despite its sophistication, the attack had a very short lifespan thanks to the vigilance of the security community.

  • September 8, 2025, ~13:16 UTC: The malicious versions of ~18 packages were first published to the npm registry. Security firm "Aikido" Security's automated intelligence feed flagged the suspicious updates almost immediately and alerted Junon via the social network Bluesky.
  • September 8, 2025, ~15:15 UTC: Less than two hours after the first malicious publish, Junon had confirmed the compromise and begun the cleanup process.
  • September 8, 2025, ~17:17 UTC: npm had officially acknowledged the breach and was working to remove the tainted packages from the registry.

This rapid, community-driven response was instrumental in containing the damage from what could have been a far more catastrophic incident.

Package Name Malicious Version Approx. Weekly Downloads
ansi-regex 6.2.1 ~243 million
ansi-styles 6.2.2 ~371 million
backslash 0.2.1 ~260,000
chalk 5.6.1 ~300 million
chalk-template 1.1.1 ~3.9 million
color 5.0.1 ~27 million
color-convert 3.1.1 ~193 million
color-name 2.0.1 ~191 million
color-string 2.1.1 ~27 million
debug 4.4.2 ~357 million
error-ex 1.3.3 ~47 million
has-ansi 6.0.1 ~12 million
is-arrayish 0.3.3 ~73 million
simple-swizzle 0.2.3 ~26 million
slice-ansi 7.1.1 ~59 million
strip-ansi 7.1.1 ~261 million
supports-color 10.2.1 ~287 million
supports-hyperlinks 4.1.1 ~19 million
wrap-ansi 9.0.1 ~198 million

The Weapon: A Deep Dive into the Crypto-Clipper Malware

The payload injected into the compromised packages was a highly specialized piece of malware known as a crypto-clipper. It was designed with a singular purpose: to steal cryptocurrency by intercepting and redirecting transactions in the victim's browser.

🥷 What is a Crypto-Clipper? The Digital Pickpocket Analogy

At its core, clipper malware, also known as "cryware" or a "ClipBanker," is a digital thief that exploits the copy-and-paste function. Imagine writing down a friend's bank account number on a slip of paper to make a transfer. As you hand the slip to the bank teller, a pickpocket deftly swaps it with another slip containing their own account number. You complete the transfer, oblivious to the change, and the money goes to the thief.

A crypto-clipper does this digitally. It silently monitors the device's clipboard, waiting for the user to copy a long, complex cryptocurrency wallet address. When it detects one, it instantly replaces it with an address belonging to the attacker. When the user pastes the address to initiate a transaction, they unknowingly paste the attacker's address, sending their funds into the wrong hands.

Under the Hood: Browser Hooking and API Interception

This specific malware was engineered to execute exclusively in a client-side browser environment, meaning it targeted end-users of websites that bundled the malicious packages, not the developers' machines or servers directly. It employed a sophisticated, two-pronged strategy to ensure maximum coverage.

  1. Passive Network Interception: The malware began by "hooking," or overriding, the browser's fundamental networking functions (fetch and XMLHttpRequest). This gave it the power to inspect all network traffic flowing in and out of the webpage. When it detected an API response containing a cryptocurrency address, it would rewrite that address on the fly before the website's legitimate JavaScript code had a chance to process or display it. more...
  2. Active Transaction Hijacking: The malware's behavior escalated if it detected the presence of a Web3 wallet extension like MetaMask, which it identified by checking for the window.ethereum object in the browser. In this mode, it moved from (above mentioned) passive interception to active hijacking. It would intercept API calls made to the wallet, such as eth_sendTransaction, and directly manipulate the transaction parameters in memory just moments before the user was prompted to sign and approve it. The user would see a legitimate-looking confirmation pop-up from their wallet, but the underlying recipient address had already been maliciously altered. more...

This dual-pronged approach was architected to exploit both the technical workings of web applications and the psychological trust a user has in their wallet's interface. By intercepting data at both the network and wallet layers, the malware ensured that even if a user was careful, the information they were verifying had already been compromised. (Holy moly, that's wild 😎)

Levenshtein Distance

Perhaps the most clever aspect of the malware was its method for choosing which of the attacker's wallet addresses to use for the swap. Instead of using a single, static address, it employed the Levenshtein distance algorithm.

This algorithm measures the "edit distance" between two strings - essentially, the minimum number of single-character edits (insertions, deletions, or substitutions) required to change one string into the other. The malware contained a pre-compiled list of attacker-controlled wallets. When a user copied a legitimate address, the malware would calculate the Levenshtein distance between that address and every address in its list. It would then select the attacker's address that was visually most similar to the original, making the swap incredibly difficult to detect with a cursory glance (without much attention to detail). This technique preys on the fact that cryptocurrency addresses are long and complex, and users rarely verify them character by character.

Hiding in Plain Sight: Code Obfuscation

To avoid immediate detection by developers or security scanners, the malicious code was heavily obfuscated.

Code obfuscation is the process of making source code intentionally difficult for humans and automated tools to understand, without altering its functionality.

Common techniques used in such malware include:

  • Symbol Renaming: Replacing descriptive variable and function names like replaceWalletAddress with meaningless single letters like x or a1.
  • String Encryption: Hiding critical strings, such as wallet addresses or targeted function names, by encoding them in formats like Base64 or hex, only to be decoded at runtime.
  • Control Flow Obfuscation: Restructuring the code's logic, turning simple conditional statements into convoluted loops (like for, while) and jumps (like break, continue, return, goto). This creates "spaghetti code" that is functionally identical but logically incomprehensible to a human analyst.

The goal was to embed a small, unreadable block of code into the otherwise legitimate package files, allowing it to slip past all but the most thorough of code reviews.

The Pattern: This Has Happened Before, and It Will Happen Again

The qix incident, while shocking in its scale, was not an isolated event. It is the latest and most prominent example in an escalating trend of software supply chain attacks targeting the open-source ecosystem. Attackers have recognized that compromising a single trusted component is a highly efficient way to infect thousands or even millions of downstream users.

The New Perimeter: Why Attackers Love Open Source

A software supply chain attack operates on a simple principle: instead of attacking a fortified castle directly, poison the well that supplies its water. In software, this means injecting malicious code into a trusted upstream dependency (i.e. a library, a framework, or a developer tool) and waiting for that code to be distributed to unsuspecting consumers.

The npm ecosystem is a particularly fertile ground for these attacks. As the world's largest software registry, it forms the backbone of modern web development. Projects often have deep and complex dependency trees, with hundreds of "transitive dependencies" pulled in automatically without the developer's direct knowledge. This complexity, combined with a culture of implicit trust, creates an environment where a single compromised maintainer account can have a catastrophic ripple effect across the entire industry.

A Rogues' Gallery of Recent npm Attacks

Analyzing recent major npm compromises reveals a clear pattern of evolving attacker sophistication. They are moving from broad, opportunistic attacks to highly strategic operations with payloads tailored to the specific context of the compromised package.

  • ua-parser-js (October 2021): In a similar account hijacking incident, an attacker gained control of the ua-parser-js package, which boasts millions of weekly downloads. They published malicious versions designed to install a Monero cryptocurrency miner on infected machines and deploy the DANABOT banking trojan on Windows systems. This attack demonstrated a similar entry vector (account compromise) but with a more generic, resource-hijacking payload. more...
  • eslint-config-prettier & is package (July 2025): These compromises were part of a sustained phishing campaign that used typosquatted domains (like npnjs.com - n instead of m) and clever social engineering to harvest maintainer credentials. The attack on the is package was particularly insidious, as the attackers phished an old maintainer and then tricked the current maintainer into re-granting them publishing rights, exploiting the trust between developers. more...
  • The nx Compromise (August 2025): This attack marked a significant leap in sophistication. After a maintainer's npm token was leaked, attackers published malicious versions of the popular nx build system. The payload was not generic; it was a highly targeted credential harvester designed to steal secrets directly from the developer's environment, including SSH keys, .npmrc files, cloud credentials, and GitHub tokens. more...

This evolution shows that attackers are no longer just dropping malware; they are strategically analyzing their targets. When they compromise a front-end utility like chalk, they deploy a browser-based payload. When they compromise a developer tool like nx, they deploy a payload designed to steal developer credentials, which can then be used to launch even more supply chain attacks. This strategic tailoring of the weapon to the target represents a dangerous new phase in the security of the open-source supply chain.

True Costs and the Lingering Threat in Your Nexus Cache

The real impact of a supply chain attack is rarely measured by the attacker's direct financial gain. Instead, it is measured in the disruption, cost, and erosion of trust inflicted upon the entire ecosystem.

Pennies Stolen, Millions Wasted

Despite the attack's enormous reach, the amount of cryptocurrency successfully stolen was surprisingly minuscule. Reports indicate the primary attacker wallet received only about 5 cents worth of ETH and $20 of a memecoin. This paltry sum stands in stark contrast to the true cost of the incident.

The real damage was a massive, industry-wide "denial-of-service" attack on productivity. Thousands of engineering and security teams across the globe were forced to drop everything, spending countless hours investigating their exposure, auditing their dependency trees, purging potentially contaminated systems, and reassuring stakeholders. This collective loss of productivity represents the true, multi-million-dollar financial impact of the attack.

Furthermore, incidents like this inflict long-term damage on the trust that underpins the open-source model. Every npm install now carries a small but palpable risk, forcing developers and organizations to adopt more defensive, time-consuming, and cautious development practices.

How Sonatype Nexus Amplifies the Threat

For many organizations, the threat did not disappear when npm removed the malicious packages from the public registry. Companies that use a local repository manager like "Sonatype Nexus" faced a hidden, persistent danger. A repository manager acts as a private, local cache for external dependencies. When a developer or a CI/CD pipeline requests a package, Nexus checks its local storage. If the package isn't there, Nexus fetches it from the public registry (like npmjs.com), stores a copy for future use, and then serves it to the requester. This process is designed to improve build speed, ensure dependency availability during public registry outages, and provide a central point for security scanning.

However, this very mechanism for resilience becomes a critical vulnerability during a supply chain attack. The sequence of events is as follows:

  1. During the two-hour window when the malicious packages were live, an internal build requests a compromised version, for example, chalk@5.6.1.
  2. Nexus, not having this version cached, downloads it from the public npm registry and stores it in its local "blob store."
  3. Nexus serves the poisoned package to the internal build, potentially compromising the resulting application.
  4. The npm security team acts swiftly and removes chalk@5.6.1 from the public registry.
  5. The Problem Persists: The malicious package still exists within the company's private Nexus cache. From this point forward, every internal developer and every CI/CD build that requests that specific version will be served the poisoned copy directly from Nexus, which no longer checks the public registry for a package it has already cached.

In this scenario, a tool designed to enhance security and reliability becomes a persistent internal vector for the malware, amplifying and prolonging the attack's window of exposure long after the public threat has been neutralized. Infrastructure designed for operational resilience can inadvertently create significant security blind spots, demonstrating a fundamental tension between the DevOps goals of speed and reliability and the security imperative of continuous verification.

A Step-by-Step Guide to Recovery and Resilience

Recovering from a supply chain attack requires a coordinated effort. The following steps are divided into three parts: immediate triage for individual developers, a guide for purging contaminated infrastructure like Nexus, and long-term strategies to harden defenses.

Part A: Immediate Triage for Your Projects (For Every Developer)

Step 1: Audit Your Dependencies

Run npm audit in the root of every project. This command checks the project's dependency tree against the npm advisory database for known vulnerabilities and will flag the malicious versions of the compromised packages.

Step 2: Manually Inspect Your Lockfiles (beyond npm ls, npm explain)

Do not rely on npm audit alone. Manually search the package-lock.json or yarn.lock file for any of the compromised packages and versions listed in the table earlier in this report. This provides definitive proof of whether a malicious version was ever installed.

Step 3: Search for Indicators of Compromise (IoCs)

If a malicious package was used in a front-end build, the malware's code may be present in the final bundled JavaScript files. Use a command-line tool like grep or your code editor's search function to scan the built application code for the following specific strings and patterns, which are known IoCs for this malware

  • Global variables: stealthProxyControl, runmask, newdlocal, checkethereumw
  • Attacker's Ethereum address: 0xFc4a4858bafef54D1b1d7697bfb5c52F4c166976

Step 4: Perform a Clean Reinstall

To ensure the environment is completely clean, perform a full reinstallation of dependencies:

  • Delete the node_modules directory and the project's lockfile (package-lock.json or yarn.lock).
  • In the package.json file, ensure dependencies are pinned to known safe versions (i.e., versions published before the incident)
  • Clear the local npm cache to remove any tainted packages stored on the machine by running npm cache clean --force.
  • Run npm install to generate a new, clean lockfile and install dependencies from a safe state.

Part B: Purging the Poison from Your Nexus Repository (For System Administrators)

Removing the malicious packages from the central Nexus cache is critical to prevent reinfection across the organization.

Option 1: The Surgical Approach (Component Deletion)

Use the search feature in the Nexus Repository UI to locate the specific malicious components by name and version (e.g., repository: npm-proxy, name: chalk, version: 5.6.1). Alternatively, use the Nexus REST API to script this search. Once located, use the "Delete component" button in the UI to remove it. This can also be done programmatically.

Option 2: The Aggressive Approach (Cleanup Policies)

  • Navigate to Administration -> Repository -> Cleanup Policies and create a new policy. Set an aggressive criterion, such as "Component Usage," to remove components that were "Last downloaded before 1 day(s)"
  • Edit the configuration of the affected repository (i.e. npm-proxy) and assign this new cleanup policy to it.
  • Manually execute the "Admin -> Cleanup repositories using their associated policies" task from the System -> Tasks menu to trigger the purge.

Mandatory Final Step: Compact the Blob Store

Both of the above methods only "soft delete" the components; the data still resides on the disk and the space is not reclaimed. To permanently remove the data, an administrator must run the "Admin -> Compact blob store" task for the relevant blob store. This is a critical and often-overlooked final step to ensure the malicious artifact is truly gone.

Part C: Hardening Your Defenses for the Future

Preventing the next attack requires adopting more resilient practices at both the developer and organizational levels.

For Developers:

  • Enforce Lockfiles: Always commit package-lock.json or yarn.lock to source control. In CI/CD environments, use npm ci instead of npm install. The ci command performs a clean install based only on the lockfile, ensuring deterministic and repeatable builds that are not susceptible to newly published malicious versions.  
  • Harden Your Accounts: Enable 2FA on all developer accounts, especially for package registries (npm) and source control (GitHub). More importantly, advocate for and adopt phishing-resistant MFA methods like FIDO2/WebAuthn security keys, which are immune to the "AitM" attack that caused this incident.

For Organizations:

  • Automate Security in CI/CD: Integrate dependency scanning directly into the CI/CD pipeline. Configure npm audit to run on every build and set a failure threshold (e.g., --audit-level=high) to automatically block code with critical vulnerabilities from being deployed.
  • Manage the Proxy: Continue using a local proxy like Nexus, but incorporate the purging procedures from Part B into the official incident response plan. Consider investing in tools like Sonatype Repository Firewall, which can automatically identify and block malicious packages from entering the cache in the first place, shifting from a reactive to a proactive defense.

Ultimately, recovery from a supply chain attack is a shared responsibility. Developers must secure their local environments and codebases, while operations and security teams must secure the shared infrastructure. A clean node_modules folder is of little use if the CI server pulls the same poison from a contaminated Nexus cache, and a pristine Nexus cache cannot protect a developer whose lockfile is already pointing to a malicious version. A coordinated, multi-layered response is the only effective path to resilience.

The Price of Trust

The qix incident serves as a powerful reminder of the fragile nature of trust in the digital supply chain. It demonstrated how sophisticated phishing can bypass standard security controls, how clever malware can exploit both technical and psychological vulnerabilities, and how infrastructure designed for efficiency can become a vector for persistent threats. The open-source ecosystem is built on a foundation of trust, but this event makes it clear that trust must now be paired with rigorous verification.

The true impact was not the theft of cryptocurrency but the theft of time, productivity, and confidence. Yet, the story also contains a lesson of empowerment. The rapid response from the security community, the transparency of the compromised developer, and the collective effort to remediate the issue highlight the ecosystem's resilience.

By understanding these attacks, adopting defensive coding practices, and advocating for stronger security measures, developers (even junior ones) are not helpless. They are the essential guardians on the front lines of the software supply chain.

Top comments (0)