PHP
APACHE
NGINX
AUDITD
DetectionBlue TeamSOCOctober 12, 2026•9 min read

Detecting PHP Web Shells on Apache and Nginx

PHP shells are the most common web shell family because PHP is everywhere and a working shell can be one line long. The reliable tell is not the code but the process the web server starts.

Scroll

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

SignalWhereATT&CK
apache2, httpd, nginx or php-fpm spawning sh, bash, curl, wget or ncauditd execve, Sysmon for Linux, EDRT1059.004
New .php, .phtml or .phar in upload, cache or theme directoriesauditd file watches, FIMT1505.003
eval, assert, base64_decode, gzinflate, str_rot13, system, passthru or shell_exec in uploaded filesFile scan, YARAT1027
POST requests to a rarely used script with tiny constant responsesAccess logsT1071.001
Web server user downloading tools to /tmp or /dev/shmauditd, EDRT1105

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

Vetted sources

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 EngineeringManaged Detection & ResponseThreat HuntingIncident ResponsePurple Teaming
  • 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.
Written by: Mjolnir Threat Research (MTAC)  |  Published: October 12, 2026  |  Last reviewed: October 12, 2026