elastic / elastic/detection-rules

[New Rule] Native Binary Creation Under node_modules

Open
#6,609 1 comment 0 reactions 0 assignees View on GitHub
community
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.