Lyrie
Defensive Playbook
0 sources verified·11 min read
By Lyrie.ai Cyber Research Division·5/9/2026

TL;DR

On April 22, 2026, a sophisticated supply chain worm dubbed Shai-Hulud: The Third Coming was discovered embedded inside @bitwarden/cli version 2026.4.0 on NPM — a package with 250,000 monthly downloads. The malware self-propagates by infecting victim NPM packages and re-uploading them with backdoored versions, while simultaneously exfiltrating every secret it can find: NPM tokens, GitHub tokens, GitHub Actions workflow secrets, and cloud provider credentials (AWS, GCP, Azure). Stolen data is encrypted with AES-256-GCM and uploaded to public GitHub repositories created on the victim's own account. A companion campaign ("Mini Shai-Hulud") extended the infection across PyPI, PHP registries, and SAP tooling packages, hitting an estimated 1,800+ developers. Checkmarx KICS was simultaneously compromised via Docker Hub and VS Code extension channels. Attribution points toward Russia via a hardcoded locale check that causes the worm to self-terminate on Russian-language systems.

If you installed @bitwarden/cli from NPM without a pinned version between late April and early May 2026: treat your machine and every credential on it as fully compromised, rotate everything, and audit your downstream packages immediately.


Background: Three Generations of the Worm

"Shai-Hulud" — named after the sandworms of Dune — did not arrive fully formed. This is the Third Coming, implying the threat actor (likely the Russian-attributed group TeamPCP) ran two prior campaigns to refine the delivery, evasion, and self-propagation mechanics before deploying against a high-trust, high-download-count target.

The first two iterations targeted lower-visibility packages to test the worm mechanism, C2 infrastructure via GitHub dead-drops, and AES-256-GCM encrypted exfiltration. By the Third Coming, the group had refined the targeting to hit a package where the trust factor was maximal: Bitwarden CLI — a password manager tool routinely installed in CI/CD pipelines to pull secrets at build time. Poisoning a credential-management tool to steal credentials is not just ironic; it is tactically brilliant. The tool's legitimate purpose provides perfect cover for the outbound traffic it generates, and many defenders whitelist network connections made by password manager binaries by policy.

The companion "Mini Shai-Hulud" campaign hit SAP CAP, mbt, Lightning, and Intercom packages on NPM along with PyPI and PHP registries, suggesting TeamPCP is running a parallel assembly line: one premium worm for high-value targets and a lightweight variant for high-volume ecosystem saturation. 1,800+ confirmed developer infections were reported within 72 hours of Mini Shai-Hulud's discovery.

A third concurrent arm of the campaign — AI coding agent targeting — modified VS Code extensions ([email protected], [email protected]) with crafted prompts designed to coerce Claude Code, Gemini CLI, GitHub Copilot, Codex, and similar LLM-powered coding assistants into exfiltrating credentials on behalf of the attacker. This is the supply chain attack merging with AI-agent abuse at scale.


Technical Analysis: How the Worm Operates

Stage 1: Entry via Preinstall Hook

The infection begins with NPM's preinstall lifecycle hook — one of the most dangerous and under-monitored surfaces in the Node.js ecosystem. The malicious package.json for @bitwarden/cli 2026.4.0 triggers bw_setup.js during the preinstall phase, which installs the Bun runtime (a fast JavaScript runtime that bypasses some Node.js-specific security controls). Once Bun is installed, bw1.js is executed.

package.json
  └─ preinstall: node bw_setup.js
       └─ installs Bun runtime
            └─ executes bw1.js [MALICIOUS PAYLOAD]

Using Bun as a secondary runtime provides two advantages: many security tools audit node process invocations but not bun, and Bun's performance allows the credential harvesting stage to complete before any process monitoring tools register anomalous behavior.

Stage 2: Locale Check (Russian Operator Safety Net)

The first thing bw1.js does is check the host system for Russian language configuration. If Russian locale (ru, ru_RU, etc.) is detected via either the Intl API or environment variables (LC_ALL, LC_MESSAGES, LANGUAGE, LANG), the malware exits cleanly with no artifacts. This is a standard Russian-origin malware fingerprint — operators protecting their development machines during testing — and is consistent with prior TeamPCP campaigns including the March 2026 OpenVSX extensions targeting AI coding agents.

Stage 3: Credential Harvest

The worm systematically harvests every secret class it can reach:

| Secret Type | Extraction Method |

|---|---|

| NPM auth tokens | ~/.npmrc parsing + validity validation against registry API |

| GitHub tokens | ~/.gitconfig, ~/.config/gh/hosts.yml, environment variables |

| GitHub Actions secrets | GitHub Actions runner environment variable enumeration |

| GitHub runner metadata | Runner ID, org, repo bindings |

| AWS credentials | ~/.aws/credentials, AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY env vars |

| GCP credentials | ~/.config/gcloud/ application default credentials |

| Azure credentials | ~/.azure/ profile, AZURE_CLIENT_SECRET env vars |

| SSH private keys | ~/.ssh/ directory scan |

All collected data is serialized to JSON, then encrypted using AES-256-GCM with an asymmetric key pair — only the threat actor holds the private key. This means defenders who find the exfiltrated GitHub repositories cannot recover the plaintext credentials from the publicly visible ciphertext.

Stage 4: Exfiltration via GitHub Dead-Drop

Rather than phoning home to a suspicious domain, the worm creates a new public GitHub repository on the victim's own account and uploads the encrypted credential package as results-TIMESTAMP-ID.json. This is operationally elegant for the attacker:

  • Traffic to github.com is almost never blocked or flagged
  • The repository appears to originate from the victim, complicating attribution
  • The public repository allows the attacker to retrieve the data from anywhere
  • Defenders seeing GitHub API calls from CI/CD environments have no immediate signal

Repository descriptions contain the hardcoded string "Shai-Hulud: The Third Coming" — an in-band identifier that researchers can search for to enumerate the full victim count. OX Security confirmed real user data being staged in such repositories at time of disclosure.

Stage 5: Self-Propagation (The Worm Mechanic)

Here the attack crosses from credential thief to autonomous worm. Once it has a valid NPM token, bw1.js:

1. Enumerates packages published by the victim account

2. Downloads the latest version of each package

3. Injects a copy of the malicious payload into the downloaded package source

4. Re-publishes an incremented version to NPM under the victim's identity

This means every developer who npm installs a package from an infected developer's registry without version pinning installs the worm on their own machine, and the cycle repeats. The exponential spread potential is substantial given the 250K monthly download baseline of the Bitwarden CLI entry vector.

Stage 6: GitHub Actions Secrets Steal

For CI environments, the worm includes a dedicated GitHub Actions secrets exfiltration module. When executed inside a GitHub Actions runner, it enumerates GITHUB_TOKEN, any secrets injected as environment variables (secrets.AWS_KEY, etc.), and the runner's identity metadata. This is particularly damaging because CI environments often hold production-grade credentials: deployment keys, cloud provisioning tokens, and registry admin tokens.


IOCs (Indicators of Compromise)

| Indicator | Type | Notes |

|---|---|---|

| @bitwarden/cli v2026.4.0 | Malicious NPM package | Safe: ≤2026.3.0 |

| bw1.js | Malicious file in package | Contains payload |

| bw_setup.js | Dropper script | Installs Bun runtime |

| GitHub repos with description: "Shai-Hulud: The Third Coming" | C2/exfil repository | Search GitHub for this string |

| File pattern: results-TIMESTAMP-ID.json | Exfil file | AES-256-GCM encrypted blob |

| Field names: envelope, key in JSON | Exfil data structure | Asymmetric AES-GCM format |

| zero[.]masscan[.]cloud | C2 domain (Mini variant) | Used in Lightning/Intercom extension |

| NPM packages: [email protected], [email protected] | VS Code extensions | AI agent credential coercion |

| Unexpected Bun binary installation | Process anomaly | Check /usr/local/bin/bun, ~/.bun/ |

| GitHub repository created by your account with unexpected name | Account abuse indicator | Audit GitHub repos created in last 30 days |

| Unexpected NPM package versions published by your account | Self-propagation indicator | Audit publish history in npmjs.com |


Lyrie Take

Shai-Hulud Third Coming represents a clear capability graduation for TeamPCP. The campaign demonstrates:

1. Worm mechanics working at scale in production NPM — not a proof of concept.

2. GitHub as a fully viable C2 channel that most enterprises cannot block without breaking development workflows.

3. AI coding agent poisoning merged into the same campaign — the attacker is not treating AI and supply chain as separate tracks; they are one unified attack surface now.

4. Asymmetric encryption of exfil data — defender access to the C2 channel yields nothing actionable; only rotation of credentials matters.

The trust problem is structural: developers and CI pipelines are conditioned to pull packages without integrity verification because the ecosystem was never designed with adversarial supply-chain actors operating at this sophistication level. That assumption is now obsolete.

The Bitwarden angle deserves particular attention. Security teams often implement "security tool" exceptions in DLP and network monitoring policies — credential managers are assumed benign, and their outbound calls to registries and APIs are whitelisted. This campaign explicitly exploits that mental model. Any tool that handles credentials is, by that fact, a premium target.


Defender Playbook: Secrets-Safe CI/CD

Immediate Response (if @bitwarden/cli 2026.4.0 was installed)

# 1. Identify affected machines
find / -name "bw1.js" 2>/dev/null
find ~/.bun /usr/local/bin/bun 2>/dev/null   # unexpected Bun runtime

# 2. Check for unauthorized GitHub repos
gh repo list --limit 200 --json name,createdAt | \
  jq '.[] | select(.createdAt > "2026-04-22")'

# 3. Check NPM publish history for unexpected versions
npm access ls-packages && npm search --maintainer YOUR_NPM_USER

# 4. Downgrade immediately
npm install @bitwarden/[email protected] --save-exact

Rotate without delay:

  • All NPM tokens (profile → Access Tokens → revoke all, issue new scoped tokens)
  • GitHub PATs and fine-grained tokens
  • GitHub Actions secrets (Settings → Secrets → regenerate)
  • AWS IAM keys / GCP service account keys / Azure app registrations
  • SSH keys present on affected machines
  • Any secrets stored in ~/.npmrc, ~/.gitconfig, ~/.aws/

Hardening Layer 1: Version Pinning and Integrity Verification

Pin every dependency, everywhere:

// package.json — ALWAYS use exact versions for security tools
{
  "devDependencies": {
    "@bitwarden/cli": "2026.3.0"  // ← exact, not "^2026.3.0"
  }
}
# package-lock.json / npm-shrinkwrap.json should be committed and verified
# Add to CI pipeline:
npm ci --ignore-scripts   # ← disable preinstall/postinstall hooks for prod deps

Use --ignore-scripts in CI unless a specific package explicitly requires install scripts you've audited. The entire Shai-Hulud attack surface is the preinstall hook. Blocking npm install scripts in your CI pipeline closes this vector entirely for production builds.

Enforce subresource integrity via lockfile attestation:

# .github/workflows/ci.yml
- name: Install dependencies (scripts disabled)
  run: npm ci --ignore-scripts
  
- name: Verify lockfile integrity
  run: |
    npm audit --audit-level=high
    npx lockfile-lint --path package-lock.json \
      --allowed-hosts npm registry.npmjs.org

Hardening Layer 2: Secrets Never Touch the Filesystem

The worm's harvest phase relies on credentials being present in standard dotfile locations (~/.npmrc, ~/.aws/credentials, ~/.gitconfig). Eliminate this by using OIDC-based short-lived credential issuance wherever possible:

# GitHub Actions: use OIDC — no static AWS keys EVER
jobs:
  deploy:
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::ACCOUNT:role/GitHubActionsRole
          aws-region: us-east-1
          # No access keys needed — OIDC exchange provides ephemeral creds

For NPM publish tokens in CI, use automation tokens scoped to single-package publish rather than full-access publish tokens. Rotate them on every release pipeline run using a secrets manager:

- name: Publish to NPM
  env:
    NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}  # ← scoped, short-lived
  run: npm publish --access public

Hardening Layer 3: Outbound Network Controls for CI Runners

The exfiltration phase requires the compromised process to create a GitHub repository (GitHub API POST) and push data to it. Implement egress controls on CI runners that restrict outbound HTTPS to an explicit allowlist:

  • Allow: registry.npmjs.org, api.github.com (GET only for package resolution)
  • Block: api.github.com POST from build steps (repository creation, commits)
  • Alert on: any new domain contacted from a build runner not in the allowlist

For self-hosted GitHub Actions runners, this can be enforced via host-level network policy or iptables. For GitHub-hosted runners, use a proxy or network monitoring layer.


Hardening Layer 4: Preinstall Script Auditing

Implement automated scanning of package.json scripts entries before installation in your CI pipeline. Tools:

  • npm-scripts-info — enumerate all lifecycle scripts before install
  • safe-npm / npm-safe-install — block install scripts unless explicitly allowlisted
  • Phylum CLI — commercial-grade NPM package behavioral analysis pre-install
  • Socket.dev — monitors packages for new install scripts, privilege escalation, network calls added in new versions
# Add to CI before npm install:
- name: Audit package scripts
  run: |
    npx @phylum/cli analyze package-lock.json \
      --threshold=critical --exit-code=1

Hardening Layer 5: Runtime Detection (What Shai-Hulud Looks Like)

In your SIEM/EDR, alert on these behavioral patterns:

| Behavior | Detection Rule |

|---|---|

| node or bun process spawned by npm install | Child process of npm with network socket |

| ~/.bun directory created unexpectedly | File creation event in home dir by package manager |

| GitHub API call to POST /user/repos from build agent | Network log: api.github.com POST from CI IP |

| ~/.npmrc read by non-interactive process during build | File audit: ~/.npmrc access by npm subprocess |

| AWS credentials file (~/.aws/credentials) read by Node.js | File audit: .aws/credentials read by non-aws-cli process |

| New NPM package version published outside release workflow | Registry monitor: publish event not tied to release pipeline |


Hardening Layer 6: GitHub-as-C2 Detection

Since GitHub is an approved domain in nearly every enterprise, detect its abuse as C2 by monitoring for:

  • Repository creation events from service accounts or CI runner identities
  • GitHub API calls that use a Personal Access Token rather than the OIDC token you explicitly issued
  • git push from build machines to repositories not in your org's approved list
  • Large (>10KB) file commits to newly-created repositories from CI identities

GitHub itself provides an audit log API that can be streamed to your SIEM: GET /orgs/{org}/audit-log?phrase=action:repo.create will catch unauthorized repository creation in real time.


Sources

1. OX Security: "Shai-Hulud: The Third Coming — Bitwarden CLI Backdoored in Latest Supply Chain Campaign" (May 5, 2026) — https://www.ox.security/blog/shai-hulud-bitwarden-cli-supply-chain-attack/

2. Unit42 / Palo Alto Networks: "The NPM Threat Landscape: Attack Surface and Mitigations" (Updated May 1, 2026) — https://unit42.paloaltonetworks.com/monitoring-npm-supply-chain-attacks/

3. Phoenix Security: "Mini Shai-Hulud: SAP CAP and mbt npm Packages Backdoored via Bun-Loaded Credential Stealer with Claude Code Persistence" (May 5, 2026) — https://phoenix.security/mini-shai-hulud-sap-cap-mbt-npm-supply-chain-bun-credential-stealer/

4. Security Boulevard: "1,800 Developers Hit in Mini Shai-Hulud Supply Chain Attack Across PyPI, NPM, and PHP" (May 3, 2026) — https://securityboulevard.com/2026/05/1800-developers-hit-in-mini-shai-hulud-supply-chain-attack-across-pypi-npm-and-php/

5. Hexnode: "Software Supply Chain Attack: Bitwarden CLI & Checkmarx KICS" (May 4, 2026) — https://www.hexnode.com/blogs/software-supply-chain-attack-bitwarden-checkmarx/


Lyrie.ai Cyber Research Division — Senior Analyst Desk

Lyrie Verdict

Lyrie's autonomous defense layer flags this class of exposure the moment it surfaces — no signature update required.