PHP web shells range from one-line eval stubs to feature-rich file managers wrapped in layers of encoding. Content matching has limits against obfuscation, so the strongest detections sit on Linux process telemetry: the web server or php-fpm launching a shell, a download tool or a network utility it has no reason to use.
Signals at a Glance
| Signal | Where | ATT&CK |
|---|---|---|
| apache2, httpd, nginx or php-fpm spawning sh, bash, curl, wget or nc | auditd execve, Sysmon for Linux, EDR | T1059.004 |
| New .php, .phtml or .phar in upload, cache or theme directories | auditd file watches, FIM | T1505.003 |
| eval, assert, base64_decode, gzinflate, str_rot13, system, passthru or shell_exec in uploaded files | File scan, YARA | T1027 |
| POST requests to a rarely used script with tiny constant responses | Access logs | T1071.001 |
| Web server user downloading tools to /tmp or /dev/shm | auditd, EDR | T1105 |
Web Server Process Lineage
On Linux the cleanest signal is a web-server parent with a shell or network utility child. Sigma covers this directly. Tune the parent list to what actually runs your PHP, such as php-fpm pools, mod_php under Apache or CGI.
title: Web Server Process Spawning Shell or Download Tool
logsource:
category: process_creation
product: linux
detection:
parent:
ParentImage|endswith:
- '/apache2'
- '/httpd'
- '/nginx'
- '/php-fpm'
- '/php-fpm8.2' # tune/validate: your PHP version
child:
Image|endswith:
- '/sh'
- '/bash'
- '/dash'
- '/curl'
- '/wget'
- '/nc'
- '/python3'
condition: parent and child
level: high
# Tune/validate: scheduled jobs or CGI scripts may legitimately shell out.
In Splunk, the same idea applies to the CIM Endpoint data model. Add a filter for the legitimate helper scripts you have verified:
| tstats summariesonly=true count min(_time) as firstTime max(_time) as lastTime
from datamodel=Endpoint.Processes
where Processes.parent_process_name IN ("apache2","httpd","nginx","php-fpm")
AND Processes.process_name IN ("sh","bash","dash","curl","wget","nc","python3","perl")
by Processes.dest Processes.user Processes.parent_process Processes.process
| `drop_dm_object_name(Processes)`
| search NOT process="*your-known-helper*" // tune/validate
| convert ctime(firstTime) ctime(lastTime)
Files, Content and Logs
- Watch upload and cache directories with file integrity monitoring or auditd watches. PHP files should almost never appear in directories meant for images or documents.
- Run YARA or grep scans for dangerous-function combinations such as
eval(base64_decode(andassert($_POST, restricted to directories that should hold only your own code. - Check ownership and timestamps: files owned by the web user and newer than your last deployment deserve a review.
- Access logs often show the shell being used: repeated POSTs to one path in an upload directory, from one or two client addresses, with identical response sizes.
- Disable risky functions in
php.iniwhere your application does not need them (disable_functions). It reduces blast radius and makes remaining usage easier to spot.
Use the SigmaHQ Linux web server child-process rules, Elastic’s Linux web shell detections, Splunk ESCU analytics for web server spawning, and the Neo23x0 signature-base PHP web shell YARA set. Baseline your own environment first: some applications call helper binaries on purpose, and a short allow-list removes most noise.
Build Detections That Fire
Mjolnir Security helps blue teams turn threat intelligence into high-fidelity detections across endpoint, network, identity and cloud telemetry.
- Detection Engineering We build and tune detections mapped to ATT&CK from your own telemetry.
- Threat Hunting as a Service Hypothesis-driven hunts for the behaviours on this page.
- 24/7 Incident Response Containment when a detection fires. Call +1 833 403 5875.
Stay ahead of emerging threats. Get notified when we publish new intelligence reports and advisories.
Subscribe to Alerts