elastic / elastic/detection-rules
[New Rule] Native Binary Creation Under node_modules
- Dominant language
- Python
- Stars
- 2.7k
- Forks
- 696
- Avg merge
- 4d 17h
- Merged PRs (30d)
- 87
Description
## Description
Detects the creation of a native executable file (no file extension, matching the shape of a stripped ELF binary) under a `node_modules` tree, outside the conventional `bin/`/`prebuilds/`/`.bin/` locations npm packages use to ship legitimate prebuilt binaries. An npm supply-chain infostealer (e.g. compiled-payload variants referenced in the motivating threat digest for this project) commonly drops a native binary at install time via a `preinstall`/`postinstall` script. This is a signal independent of the install script's own execution (Elastic's existing `execution_nodejs_pre_or_post_install_script_execution` rule) and independent of the dropped binary's process name; it fires on the artifact itself.
**Behavior-based** (a path-shape + file-type pattern), not a specific hash/IOC.
**Built against a pre-committed false-positive gate, not an ad-hoc design**: the motivating hypothesis explicitly called for archiving this idea if real testing showed an uncontrollable false-positive rate. Real FP baseline: a real `npm install esbuild` from the public npm registry, a genuinely popular package with real native binaries, produced exactly 2 real ELF binaries, both under a `bin/` subdirectory (`node_modules/esbuild/bin/esbuild`, `node_modules/@esbuild/linux-x64/bin/esbuild`). Real malicious simulation: a lab package with a `preinstall` hook copying a real ELF binary to its own package root (not under `bin/`), installed as a real dependency of a separate consumer project. A single discriminator, excluding `*/bin/*`, `*/prebuilds/*`, `*/.bin/*`, cleanly separated the two real cases: the malicious payload matched, both real esbuild binaries were excluded. **Gate passed, not archived.**
**Follow-up real testing, done specifically to firm up an initially-disclosed limitation**: the original pass only tested `esbuild`; two more commonly-cited native-addon packages were tested afterward and found not to be false-positive risks, for two different real reasons, not because they happen to also use `bin/`/`prebuilds/`. A real `npm install sharp` produces its actual native binary as `sharp-linux-x64-.node`, the standard Node.js native-addon `.node` extension, so this rule's `file.extension == ""` condition never matches it regardless of path (its bundled `libvips-cpp.so.` shared library also has a non-empty parsed extension). A real `npm install puppeteer` (with its `postinstall` script explicitly approved via `npm approve-scripts`, since npm's newer `allow-scripts` safety feature blocks it by default) downloads its real Chromium binary to `~/.cache/puppeteer/`, entirely outside the `node_modules` tree, out of this rule's scope by construction. `node-sass` remains untested: its native build failed in the test environment for an unrelated, pre-existing reason (a deprecated `distutils` Python dependency, not something specific to this rule).
## Target Ruleset
linux
## Target Rule Type
Event Correlation (EQL)
## Tested ECS Version
Validated against the `logs-endpoint.events.file*` schema (Elastic Defend / ECS file fieldset). Real capture was performed via direct filesystem inspection of real `npm install` outputs (this host's kernel doesn't support Elastic Defend's eBPF sensor for a live file-event capture) and reshaped into the equivalent ECS file-event schema for validation, noted transparently.
## Query
```eql
file where host.os.type == "linux" and event.type == "creation" and
file.path like~ "*/node_modules/*" and file.extension == "" and
not file.path like~ ("*/bin/*", "*/prebuilds/*", "*/.bin/*")
```
## New fields required in ECS/data sources for this rule?
None. Standard `file.path`/`file.extension` ECS fields.
## Related issues or PRs
None yet. Part of a small ongoing batch from a personal detection-gap research project (real-telemetry validated throughout).
## References
- https://socket.dev/blog/shai-hulud-2-0
- https://attack.mitre.org/techniques/T1195/002/
## Redacted Example Data
Real true positive (a real ELF binary dropped by a malicious package's `preinstall` hook, outside `bin/`):
```json
{
"event": { "category": ["file"], "type": ["creation"] },
"file": { "path": "/opt/app/node_modules/s33r-lab-mal-elf-pkg/payload_binary", "extension": "" }
}
```
Real negative controls (real, legitimate native-addon packages):
```json
{ "event": { "type": ["creation"] }, "file": { "path": "/opt/app/node_modules/esbuild/bin/esbuild", "extension": "" } }
```
```json
{ "event": { "type": ["creation"] }, "file": { "path": "/opt/app/node_modules/@img/sharp-linux-x64/lib/sharp-linux-x64-0.35.3.node", "extension": "node" } }
```
(the `sharp` binary is excluded by the `file.extension == ""` condition itself, not the path exclusion; it has the standard `.node` extension.)
Known, honestly-disclosed limitation: `node-sass`'s native-addon packaging convention remains untested (environment-specific build failure, unrelated to this rule). Broader testing before wide deployment is still recommended for any native-addon convention not covered by the three now-tested here (esbuild, sharp, puppeteer).
Contributor guide
Research direction
Start by locating existing Linux Event Correlation (EQL) rules in the detection-rules repository and compare their validation structure with the supplied query and ECS file-event schema. Done means adding the rule for file creation under node_modules with the stated exclusions and confirming the supplied malicious and legitimate examples behave as described.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux
- Domain
- security
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100