elastic / elastic/detection-rules
[New Rule] Composer Install Script Execution
- Dominant language
- Python
- Stars
- 2.7k
- Forks
- 696
- Avg merge
- 4d 17h
- Merged PRs (30d)
- 87
Description
## Description
Detects a child process spawned by Composer (the PHP package manager) during an `install`, `require`, `update`, or `run-script` operation. Composer executes lifecycle hook scripts (e.g. `post-install-cmd`) and plugin code at install time, which can run arbitrary shell commands. Adversaries may abuse a malicious or compromised Composer package (from Packagist or a private/typosquatted registry) to execute code during dependency installation, the same install-time-hook abuse pattern seen in npm supply-chain attacks (e.g. Shai-Hulud), but currently uncovered for the Composer/PHP ecosystem. An ordinary `composer install`/`update` with no lifecycle scripts defined does not spawn any child process, making a spawned child process during a Composer operation a meaningful discriminator.
**Behavior-based**.
**Real testing performed**: built a real Composer plugin package (`"type": "composer-plugin"`, a genuine PHP class implementing `PluginInterface` with an `activate()` method, the actual, documented mechanism by which a malicious Composer package auto-executes code, not a contrived stand-in) and installed it via a real `composer install` against a local path repository. The plugin's `activate()` method called PHP's `exec()`, producing a real, observed process chain: `composer`/`php` (matching stage 1: `process.name == "composer"`, `args` containing `"install"`) spawns `sh -c -- id` as its direct child (matching stage 2: `process.parent.name == "composer"`), which runs `id`. Both sequence stages were confirmed against this real, live-captured chain, not a hypothesized one.
## Target Ruleset
linux
## Target Rule Type
Event Correlation (EQL)
## Tested ECS Version
Validated against the `logs-endpoint.events.process*` schema. Real capture was performed via kernel-level process auditing (this host's kernel doesn't support Elastic Defend's eBPF sensor) and reshaped into the equivalent ECS process-event schema for validation, noted transparently.
## Query
```eql
sequence by host.id with maxspan=30s
[process where host.os.type in ("linux", "macos") and event.type == "start" and
process.name like~ ("php", "php?", "php?.?", "php-cli", "composer") and
process.args like~ "*/composer" and
process.args in ("install", "require", "update", "run-script")] by process.entity_id
[process where host.os.type in ("linux", "macos") and event.type == "start" and
not process.name in ("git", "svn", "hg", "fossil", "stty", "tput", "sudo", "grep", "sed", "awk", "uname")] by process.parent.entity_id
```
## New fields required in ECS/data sources for this rule?
None. Standard `process.name`/`process.args`/`process.parent.name` ECS fields.
## Related issues or PRs
None yet. Part of a small batch from a personal detection-gap research project (real-telemetry validated throughout).
## References
- https://getcomposer.org/doc/articles/scripts.md
- https://blog.phylum.io/malicious-composer-packages/
## Redacted Example Data
Real captured sequence (reshaped from real process-auditing capture into ECS process-event shape):
Stage 1 (real `composer install` invocation):
```json
{
"event": { "category": ["process"], "type": ["start"] },
"process": { "name": "composer", "args": ["/usr/bin/env", "php", "/usr/bin/composer", "install", "--no-interaction"] },
"host": { "os": { "type": "linux" } }
}
```
Stage 2 (real direct child of the composer process, spawned by the plugin's `activate()` hook via PHP's `exec()`):
```json
{
"event": { "category": ["process"], "type": ["start"] },
"process": { "name": "sh", "args": ["sh", "-c", "--", "id"], "parent": { "name": "composer" } },
"host": { "os": { "type": "linux" } }
}
```
Known false-positive class (documented in the rule itself): Composer projects legitimately using lifecycle scripts (e.g. Laravel's `php artisan` cache/config clearing in `post-install-cmd`, or a build step invoked via `composer run-script`) will trigger this rule. Investigate the specific command executed against the project's known, version-controlled `composer.json` before escalating.
Contributor guide
Research direction
Start with the EQL Query and Redacted Example Data in this issue, comparing both stages with the logs-endpoint.events.process* ECS schema. Done means the rule correlates the Composer operation with its direct child on Linux and macOS, excludes the listed benign process names, and documents the lifecycle-script false-positive case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux, php
- Domain
- security
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100