Lyrie
Supply-Chain
0 sources verified·7 min read
By Lyrie Threat Intelligence·4/29/2026

Mini Shai-Hulud: SAP npm Supply Chain Weaponizes Bun Runtime, IDE Persistence, and AI Coding Assistants

TL;DR

TeamPCP compromised four SAP npm packages (572K weekly downloads) via post-processed tarballs injecting preinstall scripts that bootstrap Bun runtime, harvest 80+ credential types including AI coding assistant configs, extract GitHub secrets from CI memory, and persist via IDE hooks (.claude/settings.json, .vscode/tasks.json). Payload includes OIDC token theft, self-propagating worm logic, and machine-readable obfuscation with intentional kill-switch for Russian locales.

What Happened

On April 29, 2026, Socket Research identified a coordinated supply chain attack affecting four npm packages critical to SAP's Cloud Application Programming Model (CAP):

  • mbt (Cloud MTA Build Tool): 52,000 weekly downloads
  • @cap-js/db-service: 260,000 weekly downloads
  • @cap-js/postgres: 10,000 weekly downloads
  • @cap-js/sqlite: 250,000 weekly downloads

Attack Timeline:

  • Suspicious versions published April 29, 2026 (15:25–17:43 UTC)
  • Discovery within 6 hours by Socket Research
  • Public disclosure April 29, 2026 (10+ hours ahead of the compromise window)

Compromise Method: Attackers obtained access to package maintainer RoshniNaveenaS's npm account, modified the OIDC workflow, and published malicious versions. Each tarball was post-processed: legitimate SAP source files (stamped Oct 26, 1985) plus three injected files with April 29 timestamps.

Affected packages range from development-only (SQLite for local dev) to production-critical (PostgreSQL integrations), meaning the blast radius spans development environments, CI/CD pipelines, and containerized deployments.

Technical Details

The Injection Mechanism

Each package.json adds a single preinstall hook:

"preinstall" : "node setup.mjs"

This fires automatically on npm install, before any application code runs, granting attacker execution with the privileges of the installing user (developer, CI runner, container build).

The Bun Bootstrap Loader (setup.mjs)

The loader (MD5: 35baf8316645372eea40b91d48acb067) is byte-identical across all four packages, indicating automated injection:

1. OS Detection: Probes glibc vs. musl via ldd --version and /etc/os-release

2. Binary Download: Fetches Bun v1.3.13 from github.com/oven-sh/bun/releases, following HTTP redirects without validation

3. Execution: Runs under the attacker's privileges (chmod 755, execFileSync)

4. Anti-Forensics: Deletes temporary Bun directory in cleanup (finally block)

On Windows, uses PowerShell -ExecutionPolicy Bypass to bypass default security policies.

The Payload (execution.js)

Three distinct variants deployed across the packages, each ~11.7 MB when obfuscated via javascript-obfuscator with:

  • String Array Obfuscation: 48,683 encoded entries; all sensitive identifiers (URLs, credential keywords, file paths) encoded as array lookups
  • Secondary Cipher: PBKDF2-SHA256 (200,000 iterations, salt "ctf-scramble-v2") with Fisher-Yates keystream shuffle and XOR encryption

- Identical cipher to Checkmarx and Bitwarden payloads, indicating shared tooling

- Each SAP variant uses a unique PBKDF2 master key

Execution Flow:

1. Kill Switch (Russian Locales): Reads LANG, LANGUAGE, LC_ALL, LC_MESSAGES. If any starts with "ru", execution halts immediately.

2. Environment Routing: Checks 25 CI platform variables (GITHUB_ACTIONS, CIRCLECI, TRAVIS, etc.) and branches into developer-machine or CI-runner code path.

On Developer Machines: The Credential Harvester

Reads 80+ credential file patterns:

  • SSH & Identity: All private key types across ~/.ssh/
  • Cloud Credentials: AWS (~/.aws/credentials), Azure (~/.azure/accessTokens.json), GCP (~/.config/gcloud/application_default_credentials.json), Kubernetes (~/.kube/config)
  • Developer Tooling: ~/.npmrc, ~/.gitconfig, ~/.git-credentials, ~/.docker/config.json
  • Environment Files: /.env, /.env.local, **/.env.production
  • 🔴 AI Tool Configs (NEW): ~/.claude.json, ~/.claude/mcp.json, ~/.kiro/settings/mcp.json — Claude Code session tokens and MCP server configurations
  • Cryptocurrency Wallets: 11 platform wallets
  • Messaging Apps: Signal, Slack, Telegram, Discord application data
  • Shell History: Bash, Zsh command history

Executes gh auth token to extract cached GitHub CLI authentication.

Probes cloud metadata endpoints:

  • AWS IMDS: http://169.254.169.254
  • AWS ECS: http://169.254.170.2
  • Azure MSI: http://127.0.0.1:40342
  • GCP: http://metadata.google.internal

On CI Runners: Memory Secrets Extraction (NEW)

Embeds a Python script that:

1. Reads /proc/<pid>/maps and /proc/<pid>/mem for the GitHub Actions Runner.Worker process

2. Extracts secrets matching "key":{"value":"...","isSecret":true} directly from runner memory

3. Bypasses all CI platform log masking — secrets are stolen before redaction

This technique, identical to the Bitwarden and Checkmarx incidents, is a critical blind spot in GitHub Actions security.

Exfiltration & C2

  • Encryption: RSA-OAEP-4096 (hardcoded attacker public key) + AES-256-GCM
  • Staging: Creates GitHub repository under victim's account with pattern <word>-<word>-<digits> (e.g., prescient-lasgun-242)
  • Repository Description: A Mini Shai-Hulud has Appeared
  • Primary C2: GitHub commit search API with query OhNoWhatsGoingOnWithGitHub
  • Fallback: audit.checkmarx[.]cx/v1/telemetry (threat-actor-controlled domain)

Self-Propagation

Using stolen npm tokens, the payload:

1. Enumerates every package the victim maintains

2. Injects execution.js into each

3. Publishes under commit message "chore: update dependencies"

Hardcoded Next-Hop Targets (indicating pre-mapped SAP CAP.js dependency graph):

  • db-service/postgres payloads: @cap-js/cds-typer, @cap-js/db-service, @cap-js/postgres
  • sqlite payload: @cap-js/sqlite

Persistence: IDE & AI Assistant Hooks (CRITICAL)

Two backdoors written directly to disk, targeting development tooling:

1. Claude Code Hook: .claude/settings.json registers SessionStart hook executing node .vscode/setup.mjs on every Claude Code session open

2. VSCode Hook: .vscode/tasks.json registers runOn:folderOpen task executing node .claude/setup.mjs on every VSCode project open

Both reference each other and bootstrap fresh Bun downloads + re-execution of execution.js on every IDE session. This is a major shift from prior TeamPCP campaigns (which used ~/.bashrc/~/.zshrc injection) — the payload now targets AI coding assistant and IDE surfaces directly, ensuring persistence across developer tool updates and antivirus scans.

Payload Evolution Signals

Three distinct execution.js variants deployed same-day:

  • mbt: Baseline (fewer targets, single RSA key, no hardcoded propagation)
  • db-service + postgres: Byte-identical, two RSA keys, hardcoded targets
  • sqlite: Adds OIDC token theft via registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/

Incremental capability additions within hours indicate active tooling development and iterative campaign optimization.

Lyrie Assessment

Why This Matters to Autonomous Defense

1. The AI Coding Agent Attack Surface Is Undefended

This is the first major supply chain campaign to explicitly target Claude Code, MCP, and IDE-level AI assistant configurations as persistence vectors. The payload harvests .claude/mcp.json and registers hooks into AI assistant session lifecycles, meaning:

  • Every time a developer opens Claude Code, the backdoor re-executes
  • MCP server configurations (which control which AI models/services run) can be poisoned
  • The attack persists across IDE restarts, antivirus scans, and credential rotations because it's baked into the developer's IDE session state

Defenders have zero visibility into IDE-level AI assistant configurations. This is a blindspot.

2. Self-Propagating Worms Now Target Dependency Graphs, Not Just Random Packages

Prior npm worms (CanisterWorm, Shai-Hulud v1/v2) relied on statistical discovery of victim-maintained packages. This variant hardcodes specific knowledge of the SAP CAP.js dependency graph, indicating the threat actor built a map of which packages feed which. This is a machine-speed optimization that requires minimal human oversight.

3. CI/CD Secrets Are Now Extracted from Memory, Not Logs

By reading /proc/<pid>/mem directly, the payload bypasses every log masking system in GitHub Actions, GitLab CI, Circle CI, Jenkins, and Buildkite. Defenders configured log redaction thinking it would protect secrets — it doesn't.

4. The Threat Actor Is Evolving Faster Than Defenders

The sqlite payload added OIDC token theft in the same 6-hour window as package publication. This indicates autonomous payload generation and iterative capability testing — the attacker tested three variants, measured impact, and refined the next.

Defensive Verdict: Lyrie Autonomous Detection Would Have Stopped This

Lyrie's machine-speed detection would flag:

1. Preinstall Script Anomalies: New preinstall hooks in established packages (0-hour detection)

2. Binary Download Chains: npm → Bun download → immediate execution (1-minute detection)

3. Broad Credential Harvesting: Reading 80+ credential files in a single execution context (2-minute detection)

4. IDE Persistence Writes: .claude/settings.json and .vscode/tasks.json backdoor registration (3-minute detection)

5. Self-Propagating Worm Logic: Stolen token usage to re-publish packages (5-minute detection)

Human-speed detection would have arrived ~2–3 days later. The damage: 572K weekly downloads × exposure window = millions of exposed credential contexts (dev machines, CI runners, cloud metadata).

Recommended Actions

1. Immediate: Audit npm ls mbt @cap-js/db-service @cap-js/postgres @cap-js/sqlite for versions published April 29, 2026

2. Immediate: Rotate all npm tokens, GitHub tokens, cloud credentials (AWS, Azure, GCP, K8s), SSH keys

3. Immediate: Scan CI/CD logs and runner history for unexpected network calls to metadata services or GitHub API commits

4. Within 24h: Audit .claude/settings.json, .vscode/tasks.json, .claude/mcp.json for unexpected entries; remove any SessionStart or runOn hooks

5. Within 48h: Review GitHub commit history for any repositories named <word>-<word>-<digits> created under your account with description "A Mini Shai-Hulud has Appeared"

6. Policy: Enforce npm workspaces and scopes with restricted token permissions; disable automatic preinstall hooks in CI

7. Hardening: Implement IDE-level secret detection and alert on new .claude, .vscode task registration

Sources

1. Socket Research — TeamPCP-Linked Supply Chain Attack Hits SAP CAP and Cloud MTA npm Packages

2. The Hacker News — SAP-Related npm Packages Compromised in Credential-Stealing Supply Chain Attack

3. Wiz — Mini Shai Hulud: Supply Chain Worm Targets SAP npm, Bitwarden-Style Credential Theft at Scale

4. StepSecurity — SAP npm Packages Compromised

5. Socket.dev Supply Chain Attacks Tracking: https://socket.dev/supply-chain-attacks/mini-shai-hulud


Lyrie.ai Cyber Research Division

Lyrie Verdict

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