Update 1745 UTC, 29 April 2026: TeamPCP attribution confirmed and new functionality documented.
A new supply chain operation from TeamPCP calling itself “Mini Shai Hulud” compromised SAP-related npm packages by injecting malicious preinstall scripts that execute during dependency installation. The campaign leverages a multi-stage payload to harvest developer and CI/CD secrets across GitHub, npm, and major cloud providers, and exfiltrates the data via attacker-controlled GitHub repositories. It also contains code designed to propagate via compromised tokens. TeamPCP is very likely responsible for this campaign, based on a shared RSA public key and overlaps in encoding routines and region guardrails.
What happened?
Malicious versions of legitimate SAP ecosystem packages (e.g., `@cap-js/sqlite`, `@cap-js/postgres`) were created by modifying them to include a `preinstall` script that executes `setup.mjs` automatically during `npm install`. This script downloads the Bun runtime and executes an obfuscated payload (`execution.js`), enabling attacker-controlled code execution prior to package installation completion.
When executed, the second-stage payload is a credential stealer and propagation framework designed to target both developer environments and CI/CD pipelines. It collects sensitive data including GitHub tokens, npm credentials, cloud secrets (AWS, Azure, GCP), Kubernetes tokens, and GitHub Actions secrets—leveraging advanced techniques such as extracting secrets from runner memory. Exfiltration occurs via public GitHub repositories, where it posts encrypted payloads. Additionally, the malware includes propagation logic to infect additional repositories and package distributions.
During its initial setup, the malware performs a system check to determine if the compromised machine is configured for the Russian language. It does this by inspecting both the system's date/time locale settings and its environment language variables. If any of these values begin with 'ru', the payload abruptly terminates itself, ensuring no data is exfiltrated from Russian-speaking systems.
While we estimate this campaign is currently active, the volume of compromised users and attacker-created exfiltration repositories appears significantly lower than that of Shai-Hulud or Shai-Hulud 2.0.
Notable Updates
This operation broadly follows the same methods used in previous TeamPCP operations, but contains a number of notable evolutions.
Github Exfiltration
In the Bitwarden operation, the malware would attempt to exfiltrate the encrypted blob of secrets to GitHub only if the primary exfiltration mechanism to a C&C domain failed. In this operation, exfiltration to GitHub is the primary and only way to exfiltrate secrets. The mechanisms are substantially similar, using the same naming scheme (word1-word2-number) and the same repo description “"Checkmarx Configuration Storage" The biggest change is that the SAP operation uses the GraphQL API, rather than the REST API, which was used in the Bitwarden operation.
Fallback to GitHub Poisoning
If the credential collection is not able to identify a gh[op]_ (PAT/OAuth) token and GITHUB_REPOSITORY is set it will attempt to poison that GitHub repository. It does this only if ghs_ or ghs_old tokens are present (and no GitHub PAT tokens are). The below files are placed in the repo, in an attempt to infect users who open the repo in Claude Code or VSCode:
| Path | Content | Purpose | Hashes |
|---|---|---|---|
| .claude/execution.js | Self-copy of the running payload via Bun.main | Payload for Claude Code users | Identical to the initial execution.js |
| .claude/setup.mjs | The dropper | Dropper that downloads Bun and executes the payload | Identical to the initial setup.mjs |
| .claude/settings.json | Claude Code hooks configuration | Claude Code SessionStart hook — runs node .vscode/setup.mjs on every session | SHA256: 14eb4ce01dd4307759887ff819359b70d7d9ff709ecde039a5abc1aac325b128 SHA1: ff7ed7a0fa1c43eed01809d076feedbaed464fc7 MD5: 00ca0c04d247ef09f2b2acc452029345 |
| .vscode/tasks.json | VS Code task configuration | VS Code task "Environment Setup" — runs node .claude/setup.mjs on folder open (runOn: folderOpen) | SHA256: 927387d0cfac1118df4b383decc2ea6ba49c9d2f98b47098bcbcba1efc026e1f SHA1: 7b0278216ac31ec18eca9eb8bc1c1261a1b26f6c MD5: dbb9b09957113463bbeb420c2c4108b5 |
| .vscode/setup.mjs | Same dropper as .claude/setup.mjs | Dropper for VS Code execution path | Identical to the initial setup.mjs |
Browser Credential Theft
The SAP operation adds the ability to steal credentials from multiple browsers (Chrome, Safari, Edge, Brave, Chromium) and exfiltrate any passwords found there. This feature was not present in any of the previous operations.
Attribution
Wiz assesses with high confidence that this is the work of the same TeamPCP operators that have previously compromised multiple libraries. This assessment is due to a shared RSA public key used to encrypt the exfiltrated secrets. This means that the same private key would decrypt the payloads, limiting the accessibility of the exfiltrated data to TeamPCP.
Additional commonalities:
The malware has an early check for the system language and if it is found to be “ru” the malware exits and does not exfiltrate any secrets. This was previously present in the Checkmarx and Bitwarden compromises.
The package uses the same npm install hook to start execution that was used in the Bitwarden CLI compromise.
GitHub based exfiltration to Dune themed repos was the fallback C2 method for the Bitwarden CLI operation, but is now the primary option.
While this operation contains references to the Shai-Hulud operations from the fall of 2025, we cannot definitively link them or say they are a separate actor.
Affected Packages
npm packages:
@cap-js/sqlite – v2.2.2
@cap-js/postgres – v2.2.2
@cap-js/db-service – v2.10.1
mbt – v1.2.48
Indicators of compromise
@cap-js/postgres 2.2.2
| File | Hash |
|---|---|
| Tarball (3,409,213 bytes) | SHA256: 1d9e4ece8e13c8eaf94cb858470d1bd8f81bb58f62583552303774fa1579edee |
| Tarball (3,409,213 bytes) | SHA1: e80824a19f48d778a746571bb15279b5679fd61c |
| Tarball (3,409,213 bytes) | MD5: e32eaf0c3cde9616831a1e92d42b0058 |
| execution.js (11,723,748 bytes) | SHA256: eb6eb4154b03ec73218727dc643d26f4e14dfda2438112926bb5daf37ae8bcdb |
| execution.js (11,723,748 bytes) | SHA1: ca4a5bb85778ffcd2153ace88fe2d882c8ceeb23 |
| execution.js (11,723,748 bytes) | MD5: b523a69b27064d1715d1f0aaffcfae63 |
@cap-js/db-service 2.10.1
| File | Hash |
|---|---|
| Tarball (3,490,641 bytes) | SHA256: 258257560fe2f1c2cc3924eae40718c829085b52ae3436b4e46d2565f6996271 |
| Tarball (3,490,641 bytes) | SHA1: 4b04304f6d51392e3f43856c94ca95800518a694 |
| Tarball (3,490,641 bytes) | MD5: d468f16eafccbc54a994f3d675ace8ae |
| execution.js | Identical to @cap-js/postgres 2.2.2 |
@cap-js/sqlite 2.2.2
| File | Hash |
|---|---|
| Tarball (3,395,651 bytes) | SHA256: a1da198bb4e883d077a0e13351bf2c3acdea10497152292e873d79d4f7420211 |
| Tarball (3,395,651 bytes) | SHA1: 7b6a28e92149637e5d7c7f4a2d3e54acd507c929 |
| Tarball (3,395,651 bytes) | MD5: 8cd683f78735c9bfc32600c73d3d9abe |
| execution.js (11,729,871 bytes) | SHA256: 6f933d00b7d05678eb43c90963a80b8947c4ae6830182f89df31da9f568fea95 |
| execution.js (11,729,871 bytes) | SHA1: bc95cc5dda788295aa0c9456791520599ef99526 |
| execution.js (11,729,871 bytes) | MD5: 6fb87d243b011b5445f379f80e1a6b4d |
mbt 1.2.48
| File | Hash |
|---|---|
| Tarball (3,373,788 bytes) | SHA256: 86282ebcd3bebf50f087f2c6b00c62caa667cdcb53558033d85acd39e3d88b41 |
| Tarball (3,373,788 bytes) | SHA1: 0af7415d65753f6aede8c9c0f39be478666b9c12 |
| Tarball (3,373,788 bytes) | MD5: 04d8a99447b16f6839fff3b978f88d7e |
| execution.js (11,678,349 bytes) | SHA256: 80a3d2877813968ef847ae73b5eeeb70b9435254e74d7f07d8cf4057f0a710ac |
| execution.js (11,678,349 bytes) | SHA1: 6bc859aaee1f8885eec2a3016226e877e5adba08 |
| execution.js (11,678,349 bytes) | MD5: 45dc9c02f82b4370ca92785282d43a86 |
Shared Dropper (all 4 packages)
| File | Hash |
|---|---|
| setup.mjs (4,549 bytes) | SHA256: 4066781fa830224c8bbcc3aa005a396657f9c8f9016f9a64ad44a9d7f5f45e34 |
| setup.mjs (4,549 bytes) | SHA1: 307d0fa7407d40e67d14e9d5a4c61ac5b4f20431 |
| setup.mjs (4,549 bytes) | MD5: 35baf8316645372eea40b91d48acb067 |
What steps should security teams take?
Immediately identify exposure: Search environments, lockfiles, artifact stores, and CI logs for affected package versions and malicious files (setup.mjs, execution.js).
Rotate all credentials: If exposure is suspected, rotate GitHub tokens, npm tokens, cloud credentials, Kubernetes tokens, and CI/CD secrets, this malware targets all of them.
Audit GitHub activity: Look for suspicious commits, newly created repositories, or indicators such as the propagation keyword and unusual commit authors.