Skip to content
← All posts

Threat Intel

Bitwarden CLI Compromised | Supply Chain Worm Steals Cloud Credentials and SSH Keys

The @bitwarden/cli@2026.4.0 npm package distributed a credential-stealing worm for 93 minutes on April 22, 2026, targeting SSH, AWS, GitHub PATs and AI tool configs. Full technical breakdown.

by SPECTROSEC Team 9 min Est. read
Supply Chain npm Credential Theft Threat Intel

On April 22, 2026, between 21:57 and 23:30 UTC, the package @bitwarden/cli@2026.4.0 on npm distributed a worm that stole cloud credentials, SSH keys, GitHub tokens and, new to this campaign wave, AI client configurations including Claude Code and Kiro. The exposure window was 93 minutes. Confirmed downloads: 334.

Context

Bitwarden CLI is the command-line client for the open-source password manager Bitwarden, with around 250,000 monthly downloads. Bitwarden confirmed that no end-user vault data was accessed. The target was the npm package used by developers in automation scripts, CI/CD pipelines and deployments.

The attack is part of the "Shai-Hulud: The Third Coming" campaign, attributed to the threat actor TeamPCP (aliases: DeadCatx3, PCPcat, ShellForce), active since 2024. Previous waves of the same worm hit 180 npm packages in September 2025 and over 640 in November 2025. This is the third iteration, and the first with explicit targeting of AI development tooling.

The C2 domain audit.checkmarx[.]cx deliberately abuses the brand of the SAST vendor Checkmarx to deceive network filters. Checkmarx is not the author: this is infrastructure-level social engineering.

Technical analysis

How the malicious code got in

The vector was not a stolen npm token. The file publish-ci.yml in the repository github.com/bitwarden/clients was directly compromised, corrupting the runner that executes npm publish. This bypasses trusted publishing and invalidates 2FA or OIDC when the pipeline is the trust anchor of the release process.

This is the most critical point of the entire story: npm token controls become irrelevant if the pipeline itself is the point of trust.

Payload execution chain

// malicious package.json (simplified)
{
  "scripts": {
    "preinstall": "node bw_setup.js"
  },
  "bin": {
    "bw": "bw_setup.js"
  }
}
  1. npm install @bitwarden/cli automatically triggers the preinstall script
  2. bw_setup.js checks if the Bun runtime is present on the system
  3. If missing, downloads bun-v1.3.13 from GitHub releases (legitimate URL, not blocked by most EDRs)
  4. Bun executes bw1.js, a 10 MB heavily obfuscated payload (seed 0x3039)

Zero user interaction. Zero prompts. Zero visible warnings.

What gets stolen

The bw1.js payload collects everything it finds:

Category Target path
SSH ~/.ssh/id_*, ~/.ssh/known_hosts
Git .git/config, .git-credentials
npm ~/.npmrc, local .npmrc
AWS ~/.aws/credentials
GCP ~/.config/gcloud/credentials.db
Azure Key Vault via cloud API
Shell history ~/.bash_history, ~/.zsh_history, .env
AI tooling ~/.claude.json, ~/.claude/mcp.json, ~/.kiro/settings/mcp.json

The addition of Claude Code and Kiro configuration files is new compared to previous campaign waves. MCP tokens and AI server configurations are now targeted with the same priority as classic cloud secrets.

Exfiltration and encryption

Stolen data is encrypted with AES-256-GCM, with the symmetric key wrapped via RSA-OAEP SHA-256. The JSON payload is compressed before encryption and sent via POST to:

https://audit.checkmarx[.]cx/v1/telemetry

If the primary domain does not respond, the worm uses GitHub as a fallback channel: it searches for public commits signed with the marker LongLiveTheResistanceAgainstMachines to retrieve subsequent C2 instructions. GitHub tokens from victims without organization membership are published directly as dead-drops in public commits, double base64 encoded.

Self-propagation

If the victim has npm tokens with write access to their own packages, the worm:

  1. Downloads the source code of the package
  2. Injects the malicious code
  3. Republishes a new version to npm under the victim's account

Each infected developer becomes a vector for their own downstream users.

Russian locale kill switch

The payload skips execution if the system has the Russian locale configured. This is not definitive proof of origin, but it is consistent with TeamPCP's historical pattern and with previous campaigns attributed to RU-based groups that avoid hitting systems in their home country.

Proof of concept

Quick check to see if you are exposed:

# Check the globally installed version
npm list -g @bitwarden/cli 2>/dev/null

# Search for markers in recent git logs
git log --oneline --all | grep -i "LongLiveTheResistanceAgainstMachines"

# Look for exfiltration repositories with Dune vocabulary names
gh repo list --limit 100 | grep -iE "sardaukar|fremen|atreides|sandworm|harkonnen|melange"

# Look for residual payload files
ls -la bun bun.exe bw1.js bw_setup.js 2>/dev/null

# Search for C2 connections in logs
grep -r "audit\.checkmarx\.cx\|94\.154\.172\.43" /var/log/ ~/.bash_history ~/.zsh_history 2>/dev/null

Real-world impact

334 downloads in 93 minutes is roughly 3-4 downloads per minute. Given that Bitwarden CLI is used primarily in CI/CD scripts and automation, each compromised machine potentially exposed:

  • Entire GitHub Actions pipeline secrets
  • AWS keys with access to S3, EC2, Lambda
  • GCP/Azure service accounts
  • npm tokens for publishing to npm (worm propagation)
  • MCP tokens for enterprise AI servers

A single developer hit in a privileged CI/CD pipeline is worth far more than 334 personal credentials.

Remediation

Immediate priority (if you used @bitwarden/cli@2026.4.0)

# 1. Remove and clean
npm uninstall -g @bitwarden/cli
npm cache clean --force

# 2. Temporarily disable npm scripts (for other packages)
npm config set ignore-scripts true

# 3. Revoke GitHub credentials
gh auth logout
# then: github.com/settings/tokens -> revoke all PATs

# 4. List and revoke npm tokens
npm token list
npm token revoke <token-id>

# 5. Rotate AWS credentials
aws configure
# Generate new access key in IAM console + revoke old one

# 6. Block C2 at firewall/proxy level
# audit.checkmarx.cx -> 94.154.172.43

Detection (including retroactive)

# IOC string grep across filesystem
rg -rn "audit\.checkmarx\.cx|LongLiveTheResistanceAgainstMachines|beautifulcastle|Shai-Hulud" .

# Verify package.json hash if you still have the npm cache
# SHA-256 of compromised package.json:
# 167ce57ef59a32a6a0ef4137785828077879092d7f83ddbc1755d6e69116e0ad

npm supply chain hardening for the future

# Disable pre/post install scripts (impacts some legitimate packages)
npm config set ignore-scripts true

# Use lock files and verify integrity
npm ci  # instead of npm install in pipelines

# Enable provenance verification (npm 9+)
npm install --foreground-scripts --strict-peer-deps

Notes from the field at SPECTROSEC

During an assessment on a fintech client, we found @bitwarden/cli used in a bash script rotating AWS keys, running nightly from a service account with iam:CreateAccessKey and iam:DeleteAccessKey. No version pinning, no lock file, plain npm install in the cron. A compromised package in that pipeline would have exposed the entire AWS infrastructure with no alert.

This is not an isolated case: Bitwarden CLI gets installed in pipelines precisely because it is trusted. And that implicit trust is exactly what TeamPCP exploited.

Another pattern we see constantly: npm tokens are treated as "low-importance tokens" compared to AWS keys or GitHub PATs. In reality, an npm token with publish access on 10 packages is a vector for 10 simultaneous supply chain attacks. Periodic rotation and least-privilege apply to npm too.

Indicators of compromise (IOC)

Type Value
Package @bitwarden/cli@2026.4.0
C2 domain audit.checkmarx[.]cx
C2 IP 94.154.172.43
Endpoint /v1/telemetry
Marker string Shai-Hulud: The Third Coming
Marker string LongLiveTheResistanceAgainstMachines
Marker string beautifulcastle
Downloaded runtime bun-v1.3.13
SHA-256 bw_setup.js (JFrog) 18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cb
SHA-256 bw_setup.js (Aikido) 37f34aa3b86db6898065f3ca886031978580a15251f2576f6d24c3b778907336
SHA-256 package.json 167ce57ef59a32a6a0ef4137785828077879092d7f83ddbc1755d6e69116e0ad

SPECTROSEC Team | professional pentesting, reach us at info@spectrosec.com https://spectrosec.com