elastic / elastic/detection-rules

[Rule Tuning] Initial Access via File Upload Followed by GET Request

Open Beginner friendly
#6,606 0 comments 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

## Link to Rule
https://github.com/elastic/detection-rules/blob/main/rules/cross-platform/initial_access_file_upload_followed_by_get_request.toml

## Rule Tuning Type
False Negatives - Enhancing detection of true threats that were previously missed.

## Description

This rule ("Initial Access via File Upload Followed by GET Request") currently scopes its `file.extension`/`url.extension` webshell-extension list to `("php", "phtml", "pht", "php5", "asp", "aspx", "jsp", "jspx", "war", "cgi")`. This misses three additional real, commonly-abused PHP-family extensions that a web server can be configured to execute exactly like `.php`: **`.phar`** (PHP Archive, executable via `phar://` stream-wrapper/handler mapping), **`.php7`** (an alternate PHP handler extension seen in real PHP 5→7 migration hosting configs), and **`.phps`** (intended as a source-display extension, but on misconfigured servers mapped directly to the PHP handler like `.php`, and a plausible deliberate choice by an attacker trying to blend in).

**Behavior-based**: this is an extension-completeness fix to an existing behavioral pattern (upload-then-access), not a new detection concept.

**Real testing performed, with a real captured 3-way signal**: stood up a real Caddy + php-fpm stack with a real upload handler (`move_uploaded_file`), issued a genuine multipart `POST /upload.php` with `Content-Disposition: form-data; filename="shell.phar"` (an inert placeholder payload), followed by a real `GET /uploads/shell.phar`. Captured three independent real signals for the same flow: Packetbeat's real HTTP body parsing of the `Content-Disposition` header, Auditbeat's real file-creation event (`php-fpm` opening the uploaded file on disk), and Packetbeat's own real ECS URL parsing (`url.extension: "phar"`, populated automatically by Packetbeat, not derived by hand). Repeated the identical flow with a real `legit.jpg` as the false-positive control.

**Validated the literal rule query, both before and after the tuning**: against the current (untuned) extension list, the real `.phar` upload-then-GET sequence produces **zero** matches, confirming this is a genuine gap, not a hypothesized one. Against the tuned list (`+phar, php7, phps`), the same real sequence produces exactly **one** match; the real `.jpg` control matches neither version, confirming the addition introduces no new false positive for ordinary image uploads.

## Example Data

Real captured sequence (Packetbeat HTTP module + Auditbeat file-creation, both genuine, not simulated):

Stage 1, real multipart upload:
```json
{
"http": {
"request": {
"method": "POST",
"body": { "content": "...Content-Disposition: form-data; name=\"file\"; filename=\"shell.phar\"..." }
}
},
"url": { "path": "/upload.php" }
}
```

Stage 2, real file creation on disk (Auditbeat, reshaped into ECS file-event shape):
```json
{
"event": { "category": ["file"], "type": ["creation"] },
"process": { "name": "php-fpm" },
"file": { "name": "shell.phar", "path": "/var/www/html/uploads/shell.phar", "extension": "phar" }
}
```

Stage 3, real subsequent GET (Packetbeat's own ECS URL parsing, not hand-derived):
```json
{
"http": { "request": { "method": "GET" } },
"url": { "path": "/uploads/shell.phar", "extension": "phar" }
}
```

Real negative control (identical flow, a real `.jpg` upload; matches neither the current nor the tuned rule):
```json
{ "url": { "path": "/uploads/legit.jpg", "extension": "jpg" } }
```

Proposed extension list addition (both occurrences in the rule, `file.extension` and `url.extension`):
```eql
("php", "phtml", "pht", "php5", "php7", "phps", "phar", "asp", "aspx", "jsp", "jspx", "war", "cgi")
```

Contributor guide

Open the contributing guide

Research direction

Open rules/cross-platform/initial_access_file_upload_followed_by_get_request.toml and inspect the two extension lists for file.extension and url.extension. Add phar, php7, and phps to both occurrences, then validate the literal rule query against the described phar upload-then-GET sequence and the jpg negative control.

Written by the indexing model from the issue text.

Assessment

Domain
security
Issue type
Bug
Difficulty
1/5
Estimated time
Under an hour
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.