How 56 npm packages used binding.gyp to steal CI/CD secrets
A highly coordinated supply chain attack compromised 56 npm packages across 286 versions by abusing the binding.gyp native build configuration to silently execute malicious code during installation. The multi-stage, heavily encrypted payload targets CI/CD environments to harvest cloud credentials, propagates via stolen OIDC tokens, and establishes persistence with a destructive dead man's switch.
- filenamecom.user.gh-token-monitor.plistmacOS LaunchAgent used as a dead man's switch.
- filenamecom.user.update-monitor.plistmacOS LaunchAgent used for C2 persistence.
- filenamegh-token-monitor.serviceSystemd service used as a dead man's switch to delete files if tokens are revoked.
- filenameupdate-monitor.serviceSystemd service used for C2 persistence, polling GitHub for commands.
- npm_packageautotel-mcpMalicious npm package with 28 compromised versions.
- npm_packageawaitlyMalicious npm package used in the campaign.
- npm_package@vapi-ai/server-sdkMalicious npm package used to deliver the Miasma payload.
Detection / HunterGoogle
What Happened
A malicious campaign flooded the npm registry with 56 compromised developer packages that silently run malicious code when installed. The attack targets software developers and automated build systems to steal sensitive cloud passwords and access tokens. This breach is highly dangerous because it bypasses standard security checks and installs a trap that deletes files if the stolen access is revoked. Organizations should immediately check their projects for the affected packages, rotate any potentially exposed passwords, and isolate infected machines before revoking access.
Key Takeaways
- An attacker published 286 malicious versions of 56 npm packages using a 'binding.gyp' file to silently execute code during installation, bypassing standard lifecycle script audits.
- The heavily obfuscated, multi-stage payload specifically targets CI/CD environments to harvest cloud credentials, secrets, and local configuration files.
- Exfiltration occurs via GitHub dead-drops using dynamically generated repository names and a spoofed Python User-Agent.
- The malware establishes persistence via two background services, including a dead man's switch that deletes the user's home directory if the stolen GitHub token is revoked.
- The payload injects malicious hooks into local AI coding assistant configurations (Claude, Gemini, Cursor, Copilot) to maintain access even after the package is removed.
Affected Systems
- Node.js environments
- CI/CD pipelines (GitHub Actions, GitLab CI, Jenkins, etc.)
- macOS and Linux developer endpoints running npm install
Attack Chain
The attack begins when a developer installs a compromised npm package, which uses a binding.gyp file to silently execute a massive, heavily obfuscated index.js file during the build phase. This loader decrypts a multi-stage payload that checks for CI/CD environments and harvests cloud credentials, secrets, and local configuration files. The malware exfiltrates stolen data to attacker-controlled GitHub repositories and attempts to propagate by injecting malicious steps into GitHub Actions workflows. Finally, it establishes persistence via two background services: a C2 monitor that polls GitHub for commands and a dead man's switch that deletes the user's home directory if the stolen GitHub token is revoked.
Detection Availability
- YARA Rules: No
- Sigma Rules: No
- Snort/Suricata Rules: No
- KQL Queries: No
- Splunk SPL Queries: No
- EQL Queries: No
- Other Detection Logic: No
The article does not provide specific detection rules but outlines behavioral signals such as the presence of a binding.gyp file alongside a large index.js in pure-JavaScript packages.
Detection Engineering Assessment
EDR Visibility: High — EDR solutions can monitor process creation (node-gyp spawning node or bun), file creation in systemd/LaunchAgents directories, and destructive commands like rm -rf. Network Visibility: Medium — Network traffic to api.github.com is encrypted and blends with legitimate developer activity, though the specific User-Agent (python-requests/2.31.0) from a Node.js process might be anomalous. Detection Difficulty: Moderate — While the initial execution blends into normal npm install behavior, the creation of persistence mechanisms and the massive size of the index.js file provide strong behavioral anomalies.
Required Log Sources
- Process Creation (Event ID 4688 / Sysmon 1)
- File Creation (Sysmon 11)
- Command Line Logging
Hunting Hypotheses
| Hypothesis | Telemetry | ATT&CK Stage | FP Risk |
|---|---|---|---|
| Hunt for npm or node-gyp processes spawning shell commands that redirect output to /dev/null while executing large JavaScript files. | Process Creation, Command Line | Execution | Low |
| Hunt for the creation of systemd services or macOS LaunchAgents containing the strings 'update-monitor' or 'gh-token-monitor'. | File Creation | Persistence | Low |
| Hunt for Node.js or Bun processes making outbound network connections to api.github.com using the 'python-requests/2.31.0' User-Agent. | Network, Process Creation | Exfiltration | Medium |
Control Gaps
- Standard npm lifecycle script auditing (preinstall/postinstall) misses binding.gyp execution.
- Network filtering often allows api.github.com for developer machines, masking exfiltration.
Key Behavioral Indicators
- Presence of binding.gyp in pure JavaScript packages.
- index.js files exceeding 4MB in npm package roots.
- node-gyp executing node index.js silently.
- Creation of /tmp/p*.js files during npm install.
False Positive Assessment
- Low. The specific combination of binding.gyp in pure JS packages, massive index.js files, and the exact persistence filenames are highly indicative of this specific campaign.
Recommendations
Immediate Mitigation
- Verify against your organization's incident response runbook and team escalation paths before acting.
- Isolate any machines that installed the affected packages BEFORE revoking compromised credentials to prevent the dead man's switch from triggering.
- Rotate all GitHub tokens, cloud provider API keys, and CI/CD secrets that were present in the environment variables of affected runners.
- Review dependency trees and lock files (using npm ls) for the specific malicious versions listed in the report.
Infrastructure Hardening
- Consider implementing automated scanning in CI/CD pipelines to block pure-JavaScript packages that contain a binding.gyp file.
- Evaluate restricting outbound network access from CI runners to only explicitly required endpoints.
User Protection
- If supported by your tooling, monitor developer endpoints for unauthorized modifications to AI coding assistant configuration files (e.g., .claude/settings.json).
- Consider enforcing strict endpoint monitoring for the creation of unexpected systemd services or LaunchAgents.
Security Awareness
- Educate development teams that malicious code can execute during npm install even if preinstall and postinstall scripts are absent.
- Remind package maintainers to enable two-factor authentication on their npm accounts to prevent credential stuffing attacks.
MITRE ATT&CK Mapping
- T1195.002 - Supply Chain Compromise: Compromise Software Supply Chain
- T1059.004 - Command and Scripting Interpreter: Unix Shell
- T1059.007 - Command and Scripting Interpreter: JavaScript
- T1027 - Obfuscated Files or Information
- T1082 - System Information Discovery
- T1552.001 - Unsecured Credentials: Credentials In Files
- T1552.004 - Unsecured Credentials: Private Keys
- T1543.001 - Create or Modify System Process: Launch Agent
- T1543.002 - Create or Modify System Process: Systemd Service
- T1071.001 - Application Layer Protocol: Web Protocols
- T1485 - Data Destruction
Additional IOCs
- File Paths:
~/.config/systemd/user/update-monitor.service- Path to the Linux persistence service.~/.config/systemd/user/gh-token-monitor.service- Path to the Linux dead man's switch service.~/Library/LaunchAgents/com.user.update-monitor.plist- Path to the macOS persistence agent.~/Library/LaunchAgents/com.user.gh-token-monitor.plist- Path to the macOS dead man's switch agent.
- Command Lines:
- Purpose: Trigger execution of the malicious payload silently during npm install. | Tools:
node-gyp,node| Stage: Execution |node index.js > /dev/null 2>&1 - Purpose: Dead man's switch payload to destroy user data if the stolen token is revoked. | Tools:
rm| Stage: Impact |rm -rf ~/Documents - Purpose: Execute the decrypted second stage payload using the Bun runtime. | Tools:
bun| Stage: Execution |bun run
- Purpose: Trigger execution of the malicious payload silently during npm install. | Tools:
- Other:
python-requests/2.31.0- Spoofed User-Agent used by the Node.js payload for exfiltration to GitHub.firedalazer- GitHub commit query string used by the update-monitor service to poll for C2 commands.Miasma - The Spreading Blight- Campaign marker string embedded in the payload.