← Back to news

Keyv and friends compromised in active Shai-Hulud supply chain attack

aikido.dev|167 points|82 comments|by cimi_|Aug 4, 2026

Shai-Hulud: A Massive Supply Chain Breach of Keyv and Caching Ecosystem

Aikido Logo

On August 4, 2026, a sophisticated supply chain attack—dubbed "Shai-Hulud"—targeted the GitHub account of a prominent maintainer. This breach allowed attackers to inject a credential-stealing worm into the keyv library (which sees 127\approx 127 million weekly downloads) and several other high-traffic caching utilities.

Because the attacker had direct access to the maintainer's account, they pushed malicious code to the main branch and triggered new releases. Consequently, these poisoned versions were published to the npm registry with valid provenance, having been signed by GitHub Actions.

🚨 The Blast Radius

The attack swept through an entire family of caching tools. The following table outlines the primary compromised packages:

Package NameCompromised VersionMonthly Downloads
keyv6.0.0604M
flat-cache6.1.24580M
file-entry-cache11.1.6571M
cacheable-request13.0.20137M
cacheable2.5.130M
@cacheable/utils2.5.134M
@cacheable/memory2.2.128M
cache-manager7.2.1016M
@cacheable/node-cache3.1.26M
@cacheable/net2.1.13.7K
ecto5.0.14.5K

Update (Aug 4, 2026, 13:37 CEST): The worm has spread aggressively. At least 434 packages across 1,381 versions are now infected, totaling over 2×1092 \times 10^9 monthly installations.

Other major organizations have also seen their packages compromised via this worm, including:

  • @deliveroo/reevent (1.0.1)
  • @or-sdk/invitations (1.4.9)
  • @picsart/ai-sdk (3.32.2)
  • @qlik/embed-runtime (1.6.4)
  • picasso.js (2.11.6)

🛠️ Technical Analysis: How it Works

The attack utilizes a multi-stage execution chain to bypass simple detection.

1. The Infection Vector

Every affected package was modified to include two new files: setup.mjs and Math_Symbol.js. The package.json was updated with a preinstall hook:

"scripts": {
  "preinstall": "node setup.mjs"
}

2. The Execution Flow

When a user runs npm install, the setup.mjs script executes automatically. Its sole purpose is to fetch the Bun JavaScript runtime and use it to launch the primary payload.

The execution command looks like this: execFileSync( bun_binary , ['script_dir / Math_Symbol .js'], { stdio: 'inherit', cwd: script_dir })

3. The Payload

Math_Symbol.js is a heavily obfuscated file (728 KB\approx 728\text{ KB}) that functions as both a credential harvester and a worm. It steals secrets and exfiltrates them to a public GitHub repository titled “Shai-Hulud: Here We Go Again.”


🔍 What is Being Stolen?

The payload contains specialized extractors for various secret stores:

📦 Package Managers & Version Control

  • npm Tokens: Scans ~/.npmrc and the filesystem for authToken values. It validates tokens against registry.npmjs[.]org/-/whoami before stealing them.
  • GitHub Tokens: Targets classic PATs (ghp_...) from ~/.config/gh/hosts.yml, environment variables, and filesystem scans.
    • Special Case: On GitHub Actions runners, it dumps the entire secret store by reading the runner process memory.
    • OIDC Theft: Steals ACTIONS_ID_TOKEN_REQUEST_TOKEN and ACTIONS_ID_TOKEN_REQUEST_URL to hijack npm publishing rights.

☁️ Cloud & Infrastructure

  • AWS Credentials:
    • Parses ~/.aws/credentials and ~/.aws/config.
    • Checks AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN.
    • Queries the EC2 Instance Metadata Service (IMDSv2 \rightarrow IMDSv1 fallback) and the ECS metadata endpoint.
    • Uses secretsmanager:ListSecrets to drain AWS Secrets Manager across regions.
  • Kubernetes: Reads the service account token and CA cert from /var/run/secrets/kubernetes.io/serviceaccount/ to query the K8s API for all namespace secrets.
  • HashiCorp Vault: Checks six sources (Env vars, ~/.vault-token, GitHub runner paths, K8s JWT, and AWS IAM auth). Once a token is found, it enumerates all KV v1 and v2 stores.

🔑 Third-Party & Local Files

  • API Keys: Specifically targets Stripe (sk_, pk_) and Slack (xox[baprs]-...) tokens.
  • Broad Filesystem Scan: A platform-aware scanner uses 200\approx 200 glob patterns to find:
    • .env, .env.*, and .envrc
    • Private keys (*.pem, *.key, *.p12, *.pfx, *.jks)
    • SSH keys (id_rsa, id_ed25519, .ssh/config)
    • Terraform state and .tfvars
    • Docker config.json
    • KeePass databases (*.kdbx)
    • VPN configs (*.ovpn)
    • IDE settings (.vscode/tasks.json, .claude/settings.json)

Files larger than 5 MB are ignored to avoid detection. A regex engine also scans for database connection strings and Azure storage keys.


🖼️ Visual Evidence

Exfiltration Process: Once the credentials are harvested, the payload encrypts the entire bundle before sending it to the attacker's repository.