elastic / elastic/detection-rules
[New Rule] Potential Webshell Command Execution via URL Query Parameter
- Dominant language
- Python
- Stars
- 2.7k
- Forks
- 696
- Avg merge
- 4d 17h
- Merged PRs (30d)
- 87
Description
## Description
Detects an HTTP request to a webshell-extension URL (`.php`, `.jsp`, `.asp`, and variants) whose query string contains a `cmd=`/`exec=`/`shell=` parameter, the standard interaction pattern for simple webshells, which typically execute the value of a query parameter as an OS command.
Checked for overlap before proposing: Elastic's existing `initial_access_file_upload_followed_by_get_request` rule detects the drop-and-first-access sequence, but not this *interaction* pattern specifically. This rule fires on every subsequent command an attacker issues through an already-dropped webshell, including ones dropped before that rule's lookback window, or where the drop itself wasn't observed/ingested. Ported from and generalized beyond Splunk `security_content`'s equivalent JSP-specific detection to cover the common webshell extension family.
**Behavior-based** (an interaction pattern), not IOC-based.
**Real testing performed**: stood up a real `nginx` + `php-fpm` stack with a genuine one-line webshell (``), issued a real `GET /shell.php?cmd=id` request (received the real command output, `uid=33(www-data)...`, confirming actual code execution), captured via a real Packetbeat HTTP module instance. As a negative control, a real `GET /shell.php?id=5` request (a benign-looking query parameter *not* named `cmd`/`exec`/`shell`) was also captured; it does not match, confirming the rule isn't simply "any query string on this extension."
## Target Ruleset
network
## Target Rule Type
Custom (KQL or Lucene)
## Tested ECS Version
Validated against `logs-network_traffic.http-*` (`url.extension`, `url.query`) via a real Packetbeat 9.4.0 HTTP-module capture, indexed and queried directly (not reshaped, this is genuine Packetbeat-native telemetry).
## Query
```kql
url.extension: ("asp" or "aspx" or "jsp" or "jspx" or "phar" or "php" or "php5" or "php7" or "pht" or "phtml") and
url.query: (
(*cmd=* or *exec=* or *shell=*) and
(*%20* or *%2B* or *%2F* or *%3B* or *%7C* or *curl* or *passwd* or *uname* or *wget* or *whoami*)
)
```
## New fields required in ECS/data sources for this rule?
None. Uses standard `url.extension`/`url.query` ECS fields already populated by Packetbeat's HTTP module.
## Related issues or PRs
None yet. Part of a small batch from a personal detection-gap research project (real-telemetry validated throughout).
## References
- https://attack.mitre.org/techniques/T1505/003/
- https://github.com/splunk/security_content
## Redacted Example Data
True positive (real Packetbeat HTTP-module capture of an actual webshell command execution):
```json
{
"url": {
"full": "http://example-app.internal/shell.php?cmd=cat%20%2Fetc%2Fpasswd",
"path": "/shell.php",
"extension": "php",
"query": "cmd=cat%20%2Fetc%2Fpasswd"
},
"http": {
"request": { "method": "GET" },
"response": { "status_code": 200 }
}
}
```
Negative control (real, same endpoint, an unrelated query parameter; does not match):
```json
{
"url": {
"full": "http://example-app.internal/shell.php?id=5",
"path": "/shell.php",
"extension": "php",
"query": "id=5"
},
"http": {
"request": { "method": "GET" },
"response": { "status_code": 200 }
}
}
```
Contributor guide
Research direction
Start with the network ruleset and compare existing custom KQL detection rules before adding this query. Validate the rule against the supplied positive Packetbeat example and confirm the shell.php?id=5 negative control does not match; done means the webshell command pattern is represented without requiring new ECS fields.
Written by the indexing model from the issue text.
Assessment
- Domain
- networking, security
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100