# Mass npm Supply Chain Attack: 20 Leo Platform Packages Compromised

Attacker compromised Leo Platform maintainer credentials and published 20 malicious npm packages in a 3-second burst, stealing CI/CD secrets via a Phantom Gyp install hook.

- Payout: Real-world hack
- Severity: critical
- Program: Leo Platform
- Analysis by: StepSecurity
- Disclosed: 2026-07-01
- Write-up quality: 7/10
- Original: https://www.stepsecurity.io/blog/mass-npm-supply-chain-attack-20-leo-platform-packages-compromised
- Library page: https://bugbountylibrary.com/r/blog%3Astepsecurity-io-blog-mass-npm-supply-chain-attack-20-leo-platform-packages-compromised
- Tags: area:oss domain:oss impact:data-leak impact:mass-exploit kind:incident platform:threat-intel sev:critical tech:github-actions tech:nodejs technique:reverse-engineering technique:source-review vuln:dependency-confusion vuln:secrets-leak

**Target:** npm packages / GitHub Actions CI/CD runners
**Root cause:** Maintainer credentials were compromised, letting the attacker publish malicious versions. The payload executes at install time via a node-gyp <!(...)> shell expansion in binding.gyp's sources array, bypassing the package.json scripts block.
**Steps:**
1. Compromise Leo Platform maintainer npm credentials
2. Publish 20 packages simultaneously with binding.gyp containing `<!(node index.js > /dev/null 2>&1 && echo stub.c)>`
3. On victim `npm install`, node-gyp executes the shell expansion, running index.js
4. index.js deobfuscates a 3-layer chain (ROT-N, AES-128-GCM, obfuscator.io) and downloads Bun v1.3.13 from GitHub Releases
5. Bun runs /tmp/p*.js which reads /proc/{pid}/mem of Runner.Worker to extract masked secrets
6. Sweep AWS/GCP/Azure/Vault/K8s/npm/PyPI/RubyGems/JFrog/GitHub PATs/1Password
7. Exfiltrate via GitHub GraphQL API commits using victim's own token (dead-drop)
8. If npm token found, republish malicious versions via bypass_2fa worm
9. Write `runner ALL=(ALL) NOPASSWD:ALL` for sudo escalation; inject workflow steps with id-token: write
**How found:** Analyst identified the 3-second coordinated publish window across 20 packages as a single automated operation, then compared structural fingerprints (binding.gyp hook syntax, 3-layer obfuscation, Bun v1.3.13 URL, 907-byte downloader blob) to the earlier Miasma campaign to attribute the same actor.
**Exploitation:** Attacker gained maintainer credentials, then used an automated factory to publish 20 packages in under 3 seconds. On any `npm install`, the Phantom Gyp hook executes the obfuscated payload, which extracts secrets from the GitHub Actions runner process memory and sweeps cloud/registry/password-manager credentials, exfiltrating them via the victim's own GitHub token to attacker repos. If an npm token is found, the worm republishes malicious versions of the victim's packages via bypass_2fa.
**Escalation:** Supply chain worm: stolen npm tokens used to publish malicious versions of any package the victim can publish, propagating the campaign. Workflow injection adds id-token: write and pinned attacker action SHAs; sudo escalation on GitHub-hosted runners.
**Insight:** A binding.gyp with a <!(...)> shell expansion in sources executes at install time without any package.json scripts entry — audit any package shipping binding.gyp with no C++ sources or .node output. Also: reading /proc/{pid}/mem of Runner.Worker recovers secrets masked in workflow logs and invisible to child processes.
