top of page

Bitwarden CLI npm Compromise: Bun-Staged Credential Stealer

  • Writer: Pranesh Shrestha
    Pranesh Shrestha
  • 4 days ago
  • 5 min read

Date Observed: April 23, 2026

Ecosystem: npm (Node.js)

Targets: Developer workstations, GitHub Actions CI/CD pipelines, cloud environments, AI coding tool configurations

Attack Type: Supply chain compromise: account hijack, OIDC Trusted Publishing abuse, malicious preinstall hook

Impact: SSH keys, GitHub/npm tokens, AWS/GCP/Azure credentials, AI tool configs, and Actions secrets exfiltrated; GitHub tokens weaponized to inject malicious workflows into downstream repositories


Key Takeaways


  • @bitwarden/cli@2026.4.0 was compromised via a hijacked Bitwarden engineer's GitHub account. The attacker abused npm OIDC Trusted Publishing to publish without a stored token.

  • A preinstall hook silently downloads the Bun JavaScript runtime and executes a 9.7 MB obfuscated credential stealer; firing on every npm install.

  • The payload explicitly targets AI coding assistant configs, MCP server files, Cursor, Kiro, Codex CLI, and Aider; a first for a confirmed npm supply chain attack.

  • GitHub-based fallback C2 channels ensure exfiltration continues even if the primary domain (audit.checkmarx.cx) is blocked.

  • Any valid GitHub token found on an affected machine is weaponized to inject malicious workflows into every accessible repository.


@bitwarden/cli@2026.4.0, the official command-line interface for the Bitwarden password manager was confirmed compromised on npm on April 23, 2026. The attack was active for approximately 1.5 hours before the compromised package was removed.


JFrog attributes this incident to the broader TeamPCP campaign, which is also linked to the Checkmarx, litellm, and telnyx PyPI compromises reported earlier in 2026. This is the first confirmed case of a supply chain attacker abusing npm OIDC Trusted Publishing as the publishing mechanism. JFrog tracks this under XRAY-969808.


After compromising a Bitwarden engineer’s GitHub account, the attacker created a new branch in bitwarden/clients, modified the publish-cli.yml GitHub Actions workflow, exchanged a GitHub Actions OIDC token for a npm auth token via the npm registry API, and then used it to publish the malicious 2026.4.0 tarball directly to npm, according to StepSecurity.


These recent ongoing campaigns highlights how even security vendors themselves; such as Checkmarx, Bitwarden, and tools like Trivy are becoming targets, creating a cascading supply chain risk where one compromise can quickly spread across other security platforms, developers, and end users who trust them.


Scope and Impact of the Attack


The malicious version 2026.4.0 was built on top of the legitimate 2026.3.0 release. The attacker injected two undocumented files: bw_setup.js and bw1.js; and added a preinstall hook. A version mismatch in the embedded metadata (package.json declares 2026.4.0; internal metadata references 2026.3.0) is the forensic fingerprint of this injection technique.


Any developer workstation, CI/CD runner, or container that ran npm install that has @bitwarden/cli in package.json during the compromise window and resolved version 2026.4.0 must be treated as potentially compromised, with immediate secret rotation and incident review. The credential surface is broad:


  • SSH access keys and trusted host connection data

  • Source code platform tokens and package publishing credentials

  • Cloud provider credentials and account access details (AWS, GCP, Azure)

  • Cloud secret storage and Key Vault access information

  • Shell command history and local credential files

  • Environment configuration files containing sensitive secrets

  • Repository access credentials and stored Git authentication data

  • CI/CD platform secrets, including accessible GitHub Actions secrets

  • AI development tool configurations and connected service credentials


How the Attack Works


Stage 1: The Loader (`bw_setup.js`)


The malicious package uses a preinstall hook to run before the normal application code, ensuring the payload executes during installation. It checks whether the Bun JavaScript runtime is available and downloads it if needed, then uses Bun instead of Node.js to run the next stage of the attack. This helps evade security monitoring that typically watches for suspicious Node.js activity during package installation.


Stage 2: The Credential Harvester (`bw1.js`)


bw1.js is 9.7 MB of obfuscated JavaScript with all strings stored in a rotated lookup array accessed via hex-indexed function calls. Three primary collectors run in parallel:


  • Collects sensitive local filesystem data including SSH keys, Git credentials, cloud configuration files, environment files, shell history, and AI tool configuration data

  • Extracts active authentication tokens from shell sessions, environment variables, and GitHub CLI authentication

  • In GitHub Actions environments, validates stolen GitHub tokens and attempts to access repositories and CI/CD secrets


Stage 3: Encrypted Exfiltration


Collected data is serialized to JSON, gzip-compressed, then encrypted using AES-256-GCM with a random 32-byte key. The AES key is wrapped with an embedded RSA public key using OAEP-SHA256. Both values are POSTed to https://audit.checkmarx.cx/v1/telemetry (IP: 94.154.172.43). The domain audit.checkmarx.cx uses the Christmas Island .cx TLD to impersonate Checkmarx in network logs performing hostname-only inspection. It has no affiliation with the legitimate security vendor.


Stage 4: GitHub-Based Fallback C2


If the primary attacker-controlled server becomes unavailable, the malware shifts to GitHub as a resilient fallback command-and-control and exfiltration channel. It uses GitHub search of commit history to retrieve attacker-controlled access tokens and updated infrastructure details, allowing operators to rotate malicious infrastructure and maintain access without updating the malware itself. Stolen data can also be exfiltrated through GitHub repositories, making the traffic appear similar to normal developer or CI/CD activity and helping it evade simple domain-based blocking.


Stage 5: GitHub Token Weaponization


For every validated GitHub token, the payload escalates from theft to active exploitation as it enumerates write-accessible repositories, lists actions secrets, creates a new branch, injects a malicious workflow, waits for the run, downloads the resulting artifact, then deletes the branch and run. A single developer install becomes the entry point for persistent workflow injection across every CI/CD pipeline the token can reach.


Why This Matters to DevOps and DevSecOps Teams


This attack breaks two assumptions that most teams rely on. First, that OIDC Trusted Publishing prevents supply chain compromise; here it was the publishing mechanism. Second, that blocking a known-bad domain is sufficient defense; the multi-channel GitHub fallback architecture is explicitly designed to survive domain blocks.


The Bun evasion technique means process-level monitoring looking for node subprocess activity during npm install will generate no alert. The 1.5-hour active window is shorter than most manual review cycles. Teams without build-time package verification and egress enforcement are fully exposed during that window. The explicit targeting of AI coding assistant configurations extends the attack surface beyond the CI/CD runner to every developer workstation where these tools are active.


How InvisiRisk Protects Against This Attack


Stability Buffer Period


InvisaRisk implements a Stability Buffer Period blocking newly released versions from immediately entering the build. This feature would not have allowed @bitwarden/cli@2026.4.0 from entering any build. BAF's configurable stability window (default: 48 hours) flags and blocks newly published package versions before they can be consumed in a build. The malicious version was live for approximately 1.5 hours; well within the default window. No pipeline running the InvisiRisk BAF would have resolved the compromised version during the attack.


Build Proxy / Network Interception


InvisiRisk’s BAF operates as a network proxy inside the CI/CD build environment, intercepting all outbound traffic. The curl POST to audit.checkmarx.cx as well as queries made during the attack are all outbound connections made during build execution. The InvisiRisk BAF can detect and block exfiltration attempts before data leaves the runner.


Unauthorized API Action Enforcement


This feature closes the GitHub token weaponization vector. InvisiRisk’s BAF enforces policy on outbound API calls during builds. POST and PUT operations that create repositories or push commits outside expected build scope are blocked; preventing the Stage 5 workflow injection from completing even if a token was already collected earlier in execution.


Conclusions


The Bitwarden CLI compromise shows how a trusted package release can quickly become a CI/CD and developer-environment security incident. By combining OIDC publishing abuse, Bun-based runtime evasion, resilient GitHub fallback channels, and GitHub token weaponization, the attackers built a package compromise that could move from a single install to broader downstream access. For teams that rely on automated builds and developer tooling, build-time verification, egress control, and real-time policy enforcement are increasingly essential.

Comments


© 2025 by InvisiRisk, Inc.

  • Twitter
  • LinkedIn
bottom of page