Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,16 @@ Defenders often monitor these scripts, so red-team reviews should also inspect l

### `binding.gyp` / node-gyp execution (Phantom Gyp)

Not every install-time execution path lives in `package.json` lifecycle hooks. `node-gyp`'s configure step looks for a `binding.gyp` file in the package directory, so a compromised publisher can shift execution into the native build path and bypass controls that only audit `preinstall` / `postinstall`.<sup>[[5]](#references)[[6]](#references)[[11]](#references)</sup>
Not every install-time execution path is an explicit `package.json` script. When a package has a root `binding.gyp` but no `install` or `preinstall` script, npm supplies an implicit `install` hook that runs `node-gyp rebuild`. The hook is also suppressed by `"gypfile": false`. The rebuild processes `binding.gyp` during configuration, before native compilation.<sup>[[5]](#references)[[27]](#references)</sup>

GYP evaluates `conditions` as Python expressions during configuration, even if the declared target has `type: none`. In the analyzed variant, a condition traversed Python's class tree to find `catch_warnings`, recovered its module's `__builtins__` despite GYP's empty builtins dictionary, and used `__import__` to reach `os.system` and launch a script. Unicode escapes concealed the names and command. A comparison consumed the call's return value, making execution a side effect of evaluating the condition. Unlike earlier `<!(...)` command expansions, this path is easy to miss when scanning only for expansions or readable commands.<sup>[[26]](#references)[[28]](#references)</sup>

**This is not an automatic bypass of `--ignore-scripts`.** npm's implicit `node-gyp rebuild` is an install lifecycle hook, which `--ignore-scripts` suppresses. An explicit `preinstall` or `install` script also prevents npm from adding that hook. If a package has both a `preinstall` script and a poisoned `binding.gyp`, a separate invocation of `node-gyp` is needed to evaluate the GYP expression; an install with `--ignore-scripts` does not do so by itself.<sup>[[27]](#references)</sup>

Practical checks:

- Inspect the **published tarball**, not only the Git repo, for unexpected `binding.gyp`, `node-gyp`, or native-addon metadata in packages that should be pure JavaScript.<sup>[[5]](#references)[[11]](#references)</sup>
- Treat a sudden `binding.gyp` addition as an execution primitive, especially if defenders rely on lifecycle-hook monitoring or `--ignore-scripts`.<sup>[[6]](#references)[[10]](#references)[[11]](#references)</sup>
- In `binding.gyp`, inspect `conditions` as well as `<!(...)` expansions; decode Unicode escapes before reviewing class or builtins access.<sup>[[26]](#references)[[28]](#references)</sup>
- Review release jobs that run `npm install`, `npm rebuild`, or dependency build steps after restoring untrusted artifacts/caches.<sup>[[2]](#references)[[11]](#references)</sup>

## Runtime Backdoors at Plaintext Secret Boundaries
Expand Down Expand Up @@ -258,6 +262,8 @@ Useful behavior-level signals from the same chain include:<sup>[[18]](#reference
- Review repository-scoped developer automation files such as `.vscode/tasks.json` and `.claude/settings.json` as executable supply-chain inputs, and protect non-default release branches as rigorously as `main`.<sup>[[18]](#references)</sup>
- Monitor unexpected runtime bootstrap downloads and package-manager child processes rather than limiting detections to Node.js internals; block release jobs from fetching unpinned runtimes where the build does not require them.<sup>[[18]](#references)</sup>

{{#include ../../../banners/hacktricks-training.md}}

## References

- [1] [What the Miasma campaign reveals about the new supply chain threat model and the underground market for developer credentials](https://www.tenable.com/blog/what-the-miasma-campaign-reveals-about-the-new-supply-chain-threat-model-and-the-underground)
Expand Down Expand Up @@ -285,5 +291,6 @@ Useful behavior-level signals from the same chain include:<sup>[[18]](#reference
- [23] [OVHcloud CLI supply chain compromise analysis](https://opensourcemalware.com/blog/glassworm-hacks-ovh-clouds-cli-tool)
- [24] [Major Shai Hulud campaign strikes npm again, affecting keyv and 400+ packages | JFrog](https://research.jfrog.com/post/shai-hulud-is-back-august/)
- [25] [Equation of Compromise: Anatomy of a Live npm Supply-Chain Campaign | JFrog](https://research.jfrog.com/post/equation-of-compromise/)

{{#include ../../../banners/hacktricks-training.md}}
- [26] [Shai-Hulud Trinitite Hits @7nohe/openapi-react-query-codegen | JFrog](https://research.jfrog.com/post/shai-hulud-trinitite/)
- [27] [npm-rebuild | npm Docs](https://docs.npmjs.com/cli/v11/commands/npm-rebuild/)
- [28] [GYP condition evaluation source | node-gyp](https://github.com/nodejs/node-gyp/blob/main/gyp/pylib/gyp/input.py)
Loading