Bitwarden CLI Backdoored in Ongoing TeamPCP Campaign — Shai-Hulud: The Third Coming

Introduction

For roughly ninety minutes on the evening of April 22, 2026, the official @bitwarden/cli npm package shipped a backdoor. Version 2026.4.0 — live from 5:57 PM to 7:30 PM ET — embedded a credential-stealing loader that swept developer machines and CI runners for cloud secrets, GitHub and npm tokens, SSH keys, shell history, and session data tied to authenticated AI coding assistants. Bitwarden has linked the compromise to the same TeamPCP-run Checkmarx supply chain campaign that hit the KICS Docker Hub images the day before. The malware's dead-drop marker: a string reading "Shai-Hulud: The Third Coming".

What Happened

Bitwarden CLI is not just a consumer password manager companion. It is the component that turns Bitwarden into developer infrastructure: teams wire bw into CI/CD pipelines to inject secrets at build time, pull API keys into scripts, and automate vault access. That's exactly what made it a priority target.

According to analyses from JFrog, Socket, Endor Labs, and OX Security, the malicious 2026.4.0 release introduced two new files — bwsetup.js (later referenced as bw_setup.js in some reports) and bw1.js — and modified package.json to add a preinstall script (node bwsetup.js) and redirect the bw entry point from build/bw.js to the new loader. A tell-tale version skew in the embedded build/bw.js metadata (still reading 2026.3.0) is an artifact that package-integrity tooling could have flagged before install.

When the preinstall fired, the loader checked whether the Bun runtime was present. If not, it downloaded Bun (attributed domain bun-*.com/.../bun/...) and used it to execute an obfuscated JavaScript payload (bw1.js). That payload:

  1. Harvests credentials across six distinct surfaces: npm and GitHub tokens, .ssh, .env files, shell history, GitHub Actions runtime secrets, and cloud credentials for AWS, Azure, and GCP — plus a dedicated module that goes after authenticated AI coding assistants.
  2. Self-propagates as a worm. With npm tokens in hand, it re-infects every package the victim can publish to.
  3. Uses GitHub commits as C2. The malware creates a repository on the victim's GitHub account — names generated from Dune-themed word lists (sardaukar, fremen, atreides, harkonnen, sandworm, ornithopter, heighliner…) suffixed with a number — and commits encrypted batches to results/results-<ts>.json (max 30 MB per commit), with the description "Shai-Hulud: The Third Coming". If the victim has no org memberships, the repo is public and the commit message encodes the stolen token back into a LongLiveTheResistanceAgainstMachines:<base64> pattern, which the attacker watches via GitHub search.
  4. Encrypts exfiltration. AES-256-GCM hybrid encryption, with a secondary signed (RSA/SHA-256) C2 channel delivered as commit messages beginning with beautifulcastle. The same audit.checkmarx[.]cx/v1/telemetry endpoint and __decodeScrambled(0x3039) obfuscation routine tie it to the concurrent Checkmarx compromise.
  5. Persists via shell RC modifications on Linux and macOS.

Bitwarden confirmed in its advisory that the compromise used a malicious GitHub Action (checkmarx/ast-github-action) that was wired into Bitwarden's CI/CD pipeline. When that action ran with publish credentials, it minted the poisoned release and pushed it to npm. Researcher Adnan Khan notes this is believed to be the first compromise of a package using npm's trusted-publishing mechanism — a pattern teams should factor into their threat model.

End-user vault data was not accessed. The compromise was limited to the npm distribution channel for the CLI during the 90-minute window, and the legitimate codebase was untouched. A CVE for @bitwarden/[email protected] is being issued, and a clean 2026.4.1 replacement is available.

Why It Matters

A backdoored password manager CLI is as close to a skeleton key as the npm ecosystem offers. bw runs inside the build systems of teams that consciously decided not to keep secrets on disk — the tool is the gate. Compromising it yields GitHub tokens, npm automation credentials, cloud provider keys, and the ability to inject malicious workflows into downstream repositories. Combined with the KICS Docker Hub incident of April 22 and the CanisterWorm npm worm, this is the third active TeamPCP wave in five days. The attackers are marching down the list of developer-trusted tooling.

Who Is Affected

  • Any machine or CI runner that npm installed @bitwarden/[email protected] during the 90-minute window (April 22, 5:57–7:30 PM ET)
  • Developers using @bitwarden/cli globally with unpinned version ranges (latest, ^2026.4.0, ~2026.4.0)
  • CI/CD pipelines that auto-updated Bitwarden CLI within the window
  • GitHub accounts belonging to impacted users — especially those with broadly scoped PATs
  • Any project whose npm publish tokens were reachable on an infected host

How to Protect Yourself

Step 1: Uninstall the bad version immediately and clear the cache.

npm uninstall -g @bitwarden/cli
npm cache clean --force
npm config set ignore-scripts true   # temporarily disable postinstall

Step 2: Install the clean replacement.

npm install -g @bitwarden/[email protected]
bw --version   # confirm

Step 3: Hunt for compromise on affected hosts.

find / -name "bw1.js" -o -name "bwsetup.js" -o -name "bw_setup.js" 2>/dev/null
ls -la ~/.local/share/bun ~/.bun 2>/dev/null
grep -r "LongLiveTheResistanceAgainstMachines" ~ 2>/dev/null
grep -r "Shai-Hulud" ~ 2>/dev/null
cat ~/.bashrc ~/.zshrc ~/.profile | grep -Ei "bun|bw1|bwsetup"

On Windows:

Get-ChildItem -Recurse -Path $env:USERPROFILE -Include bw1.js,bwsetup.js,bw_setup.js -ErrorAction SilentlyContinue
Get-Content $PROFILE -ErrorAction SilentlyContinue | Select-String -Pattern 'bun|bw1|bwsetup'

Step 4: Rotate every secret on affected hosts. Anything the infected process could reach is gone:

  • npm token revoke <id> for every npm token on the box
  • GitHub PATs — revoke under Settings → Developer settings → Personal access tokens
  • Cloud provider keys — aws iam create-access-key / az ad sp credential reset / gcloud iam service-accounts keys create
  • SSH keys — regenerate and re-register the public portion
  • Any secrets that were in .env, .envrc, direnv state, or shell history

Step 5: Check your GitHub account for exfiltration artifacts.

gh repo list --limit 200 | grep -Ei 'sandworm|sardaukar|fremen|atreides|harkonnen|ornithopter|heighliner|mentat'
gh api "search/commits?q=LongLiveTheResistanceAgainstMachines" -H "Accept: application/vnd.github.cloak-preview"

Delete any repos you did not create. Rotate any PATs whose scopes could have been used to create them.

Step 6: Block the exfiltration infrastructure at your egress firewall and DNS.

audit.checkmarx.cx
94.154.172.43

Alert on any outbound GitHub API call matching:

GET /search/commits?q=LongLiveTheResistanceAgainstMachines

Step 7: Harden the pipeline.

  • Pin npm dependencies by exact version or integrity hash — never @latest in CI.
  • Use npm ci --ignore-scripts where you do not need lifecycle hooks.
  • Replace long-lived NPM_TOKEN / GITHUB_TOKEN values with OIDC-based short-lived tokens wherever possible.
  • Treat third-party GitHub Actions like unvetted code: pin by full commit SHA, review transitive dependencies, and restrict the workflow permissions to the minimum needed.
  • If you use checkmarx/ast-github-action or checkmarx/kics-github-action anywhere, audit those workflows per Checkmarx's current advisory and pin only to verified post-remediation tags or SHAs.

Source