binding.gyp npm CI/CD worm
Summary
StepSecurity reported an active Miasma / Shai-Hulud-descended npm supply-chain worm in June 2026 that uses a small binding.gyp file to trigger install-time execution through npm's native-addon build path instead of obvious package.json lifecycle scripts. StepSecurity calls the bypass Phantom Gyp.
In the June 3--4 wave, StepSecurity counted 57 npm packages and 286+ malicious versions published in under two hours. The largest named victim was @vapi-ai/server-sdk, followed by ai-sdk-ollama and packages in the autotel, awaitly, executable-stories, node-env-resolver, and wrangler-deploy families.
The payload harvests developer and CI/CD credentials, scrapes GitHub Actions runner memory, abuses GitHub repositories as encrypted credential dead drops, injects AI-assistant and editor configuration backdoors, and uses stolen npm or RubyGems publishing access to republish poisoned package versions.
Snyk separately tracks the incident as Node-gyp Supply Chain Compromise - June 2026, classifies the affected releases as critical embedded malicious code, and warns that malicious versions remained resolvable from the public npm registry at the time of its June 4 writeup.
Tags
- ops
- operations
- malware
- supply-chain
- npm
- RubyGems
- GitHub Actions
- CI/CD
- credential-theft
- worm
- node-gyp
- binding.gyp
- Phantom Gyp
- Miasma
- Shai-Hulud
- secrets
- AI assistants
Why this matters
binding.gypgives the actor an install-time execution path that can be missed by controls focused onpreinstall,postinstall, and otherpackage.jsonlifecycle scripts.- The worm targets the same high-value identity surfaces defenders now expect from Shai-Hulud-style campaigns: package-registry tokens, GitHub tokens, cloud credentials, Vault, Kubernetes, password managers, and GitHub Actions runner memory.
- It modifies repositories, including AI-assistant/editor configuration files and GitHub setup files, so remediation requires repository review, not just package removal and token rotation.
- It reportedly propagates through both npm and RubyGems publishing access, making it a cross-registry software-supply-chain incident rather than a single package-family compromise.
Reported chain
Install-time trigger through binding.gyp
- StepSecurity says the malicious packages add a 157-byte
binding.gypfile. - When npm sees
binding.gyp, npm can invokenode-gyp rebuildduring install even when the package has no suspiciouspackage.jsonlifecycle hook. - The malicious
binding.gypuses gyp command substitution to runnode index.js > /dev/null 2>&1 && echo stub.c, returning a fake source file so the build path does not immediately fail. - The small
binding.gypleads to a multi-megabyte rootindex.js; in StepSecurity'sexecutable-stories-demo@0.1.11example, the legitimate package entry point remaineddist/index.js, while the rootindex.jsexisted only for the install trigger. - The JavaScript uses a ROT-N Caesar cipher and
eval()to decode an inner script; StepSecurity observed different ROT shifts across packages as an evasion technique. - The inner script decrypts two AES-128-GCM payloads with hardcoded keys.
Runtime staging
- The first decrypted payload downloads Bun JavaScript runtime v1.3.13 from GitHub into a temporary directory such as
/tmp/b-*. - StepSecurity says the second decrypted payload is a 668 KB obfuscated main payload that runs through the downloaded Bun runtime.
- Using Bun gives the actor a standalone execution environment while keeping the initial package modification small and bypassing monitoring that keys only on Node.js child processes.
- In StepSecurity's controlled GitHub Actions run,
npm install @vapi-ai/server-sdk@1.2.2invokednode-gyp rebuild, rannode index.js, downloaded Bun, launched/tmp/.../bun run /tmp/...js, calledgh auth token, usedsudo python3to read the GitHub ActionsRunner.Workerprocess memory, and began exfiltration toapi.github.comwithin seconds.
Credential collection
StepSecurity reports the worm searches for:
- npm tokens.
- GitHub tokens and personal access tokens.
- AWS access keys, including IMDSv2 and ECS task-role sources.
- GCP service-account credentials.
- Azure client secrets and Key Vault contents.
- HashiCorp Vault tokens from multiple local paths and the local Vault API.
- Kubernetes service-account tokens.
- RubyGems API keys.
- Passwords from 1Password CLI,
gopass, andpass. - Masked secrets extracted from GitHub Actions runner process memory.
StepSecurity specifically observed a runner-memory scraping pipeline using tr -d '\0' and grep for GitHub Actions secret structures, a technique intended to bypass log masking by reading secret values where the runner process stores them unmasked.
AI assistant and editor backdoors
- StepSecurity says the payload can commit backdoor configuration files into repositories reachable with stolen GitHub tokens.
- Named targets include Claude Code (
.claude/setup.mjs,.claude/settings.json), Cursor (.cursor/rules/setup.mdc), Gemini (.gemini/settings.json), VS Code (.vscode/tasks.json,.vscode/setup.mjs), and GitHub setup code (.github/setup.js). - The social-engineering text reported by StepSecurity frames the files as required for IDE integration and dependency setup.
- This is durable because the poisoned repository can trigger later when a developer opens the project in an AI-assisted IDE, even if the original package install is no longer happening.
GitHub repository abuse
- StepSecurity traced exfiltration to the GitHub account
liuende501, which it says hosted 236 programmatically created repositories used as credential dead drops. - The malware creates private repositories under that account and uploads encrypted JSON files under
results/results-{timestamp}.json. - StepSecurity reported repository descriptions including
Miasma - The Spreading Blightand the reversed stringniagA oG eW ereH :duluH-iahS, which readsShai-Hulud: Here We Go Again. - Reported GitHub API patterns include commit-search beacons for
thebeautifulmarchoftime, token-validation searches forIfYouInvalidateThisTokenItWillNukeTheComputerOfTheOwner, authenticated/userchecks, repository creation, content uploads, and GraphQLcreateCommitOnBranchcalls.
Package-registry propagation
- With stolen npm or RubyGems tokens, the worm queries registry accounts for packages the victim maintains.
- It downloads maintained packages, injects the malicious payload, and publishes new poisoned versions.
- For npm, StepSecurity reports token validation via
/-/whoami, maintainer package enumeration, OIDC token exchange, tarball manipulation, and publication of repackaged releases. - StepSecurity says the payload can request Fulcio signing material, create Rekor transparency-log entries, and generate SLSA v1 provenance attestations, making provenance checks insufficient if the attacker controls the publishing identity or OIDC path.
- For RubyGems, StepSecurity reports injection into Ruby native-extension build files such as
extconf.rb, with relatedMakefile.PLandCMakeLists.txtvariants in the payload. - StepSecurity listed compromised versions published between June 3 and June 4, 2026 and noted that its list was still being updated.
- Early named package families in the StepSecurity table included
@vapi-ai/server-sdk,ai-sdk-ollama, manyautotel-*packages,awaitly-*packages,executable-stories-*packages,node-env-resolver*, andwrangler-deploy.
Defender heuristics
Package review
- Treat unexpected
binding.gypadditions as install-time code-execution signals, even whenpackage.jsonscripts are absent or unchanged. - Diff newly published package tarballs against prior known-good versions and flag tiny build-configuration files that launch larger staged scripts.
- Expand package-security checks beyond lifecycle hooks to include
node-gyp, native-addon build files, generated project files, and build-tool configuration. - Flag root-level multi-megabyte
index.jsfiles that are not the declared package entry point. - Review exposure to the package names and versions StepSecurity lists; the list was still changing at publication time, so use the source as the live reference.
CI/CD and repository response
- Search CI logs for unexpected
node-gyp rebuild,curlorunzipundernpm install, Bun downloads fromgithub.com/oven-sh/bun/releases/download/bun-v1.3.13/,gh auth token, and attempts to read/proc/*/mem. - Review push events and workflow-file changes from accounts that also have npm or RubyGems publisher rights.
- Check for GitHub API activity that creates new repositories, uploads
results/results-*.json, searches commits forthebeautifulmarchoftime, or uses GraphQLcreateCommitOnBranchunexpectedly. - Audit AI-assistant and editor config paths:
.claude/,.cursor/,.gemini/,.vscode/, and.github/setup.js. - Preserve logs before rotating secrets if active repository backdoors may still be present.
Secret rotation and containment
- Rotate npm, RubyGems, GitHub, cloud, Vault, Kubernetes, and password-manager-derived credentials only after removing known repository persistence and isolating infected developer or CI hosts.
- Revoke package-registry automation tokens and validate maintainers, 2FA posture, and trusted-publishing configuration for affected packages.
- Audit all packages maintained by any compromised publisher account; the worm's propagation model means sibling packages may be poisoned even if the originally installed package was cleaned.
Reported indicators
binding.gypSHA-256:ef641e956f91d501b748085996303c96a64d67f63bfeef0dda175e5aa19cca90- Example
executable-stories-demo@0.1.11package tarball SHA-256:288f26c2eadcb1a7923fe376d16f5404216cce15d9fc162a4a78574dc7df399a - Decrypted Bun loader SHA-256:
ceff7c51d70832c3ec8dd2744b606a23b3c924ef664ae23439b9b742ea154108 - Decrypted main payload SHA-256:
da39146ef451d1b174a24d00b1e2a45cd38d54e849737f8f35333dcb22175707 - GitHub exfil account:
github.com/liuende501 - GitHub commit-search C2 keyword:
thebeautifulmarchoftime - GitHub token-validation keyword:
IfYouInvalidateThisTokenItWillNukeTheComputerOfTheOwner - Bun download path:
github.com/oven-sh/bun/releases/download/bun-v1.3.13/ - Suspicious gyp command marker:
<!(node index.js > /dev/null 2>&1 && echo stub.c)
Related pages
- Mini Shai-Hulud npm/PyPI worm campaign
- IronWorm npm Rust infostealer campaign
- GitHub Actions deployment poisoning
- TeamPCP
Sources
- StepSecurity: https://www.stepsecurity.io/blog/binding-gyp-npm-supply-chain-attack-spreads-like-worm
- Snyk: https://snyk.io/blog/node-gyp-supply-chain-compromise-self-propagating-npm-worm-binding-gyp/