Supply Chain Campaign Targets SAP npm Packages with Credential-Stealing Malware

Detect and mitigate malicious npm packages linked to the recent Shai-Hulud-style campaign - Mini Shai Hulud.

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.

Compromised users by hours, UTC timezone

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.

Compromised repositories by hours, UTC timezone

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:

PathContentPurposeHashes
.claude/execution.jsSelf-copy of the running payload via Bun.mainPayload for Claude Code usersIdentical to the initial execution.js
.claude/setup.mjsThe dropper Dropper that downloads Bun and executes the payloadIdentical to the initial setup.mjs
.claude/settings.jsonClaude Code hooks configuration Claude Code SessionStart hook — runs node .vscode/setup.mjs on every session

SHA256: 14eb4ce01dd4307759887ff819359b70d7d9ff709ecde039a5abc1aac325b128  SHA1: ff7ed7a0fa1c43eed01809d076feedbaed464fc7   MD5: 00ca0c04d247ef09f2b2acc452029345

.vscode/tasks.jsonVS Code task configurationVS Code task "Environment Setup" — runs node .claude/setup.mjs on folder open (runOn: folderOpen)

SHA256: 927387d0cfac1118df4b383decc2ea6ba49c9d2f98b47098bcbcba1efc026e1f  SHA1: 7b0278216ac31ec18eca9eb8bc1c1261a1b26f6c  MD5: dbb9b09957113463bbeb420c2c4108b5

.vscode/setup.mjsSame dropper as .claude/setup.mjs Dropper for VS Code execution pathIdentical 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

FileHash
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

FileHash
Tarball (3,490,641 bytes)SHA256: 258257560fe2f1c2cc3924eae40718c829085b52ae3436b4e46d2565f6996271
Tarball (3,490,641 bytes)SHA1: 4b04304f6d51392e3f43856c94ca95800518a694
Tarball (3,490,641 bytes)MD5: d468f16eafccbc54a994f3d675ace8ae
execution.jsIdentical to @cap-js/postgres 2.2.2

@cap-js/sqlite 2.2.2

FileHash
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

FileHash
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)

FileHash
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.

References

Continue reading

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management