When critical PHP vulnerabilities make headlines, the advisory text usually undersells the operational reality: these flaws are not theoretical, they are not hard to exploit, and patching them is rarely as simple as clicking "Update." The current wave of so-called PHP2Shell attacks — threat actors chaining PHP weaknesses into remote command execution and web shell deployment — is a textbook example. Internet-facing PHP servers are being probed within hours of public exploit availability, and defenders who assume "we patched, we're fine" are finding post-exploitation artifacts weeks later.
This post breaks down what the PHP2Shell threat actually looks like, how to determine whether your environment is genuinely exposed (versus merely running a vulnerable version), and how to hunt for the web shells that attackers leave behind — because by the time you patch, the intrusion may already have happened.
Why This Is Urgent
PHP powers the majority of the web's server-side stack — WordPress, Drupal, Joomla, Laravel applications, and countless custom line-of-business apps. When a critical PHP flaw drops, three things happen in rapid succession:
- Public proof-of-concept code appears, often within 24–72 hours of disclosure.
- Mass scanning begins immediately. GreyNoise and honeypot telemetry consistently show exploitation attempts scaling within hours, not days.
- Post-exploitation follows a predictable pattern: initial command execution → reconnaissance (
whoami,ipconfig,uname -a) → web shell upload → persistence and pivoting.
The Dark Reading reporting on PHP2Shell makes a point that experienced IR teams already know: fixing the vulnerability does not evict the attacker. If your server was exposed and unpatched for any window of time, you must assume compromise and hunt accordingly. Patch-and-forget is how six-month dwell times happen.
Technical Analysis: How PHP2Shell Attacks Work
Affected Surface
While specific affected versions vary by flaw, the highest-risk configurations share common traits:
- PHP running in CGI mode (php-cgi.exe) — particularly on Windows/IIS and XAMPP stacks, where argument-injection style flaws have historically allowed attackers to pass attacker-controlled command-line switches to the PHP binary itself.
- Apache with mod_php or PHP-FPM behind nginx/Apache reverse proxies on Linux.
- Out-of-support PHP branches (7.x and early 8.x builds) that no longer receive security fixes but remain common in legacy hosting and vendor appliances.
- Internet-exposed admin panels and frameworks (WordPress plugins, phpMyAdmin, legacy CMS installs) that amplify a PHP flaw into full RCE.
The Attack Chain (Defender's View)
From dozens of IR engagements involving PHP exploitation, the chain is remarkably consistent:
- Initial access: A crafted HTTP request reaches the PHP interpreter — either injecting arguments into
php-cgi.exeinvocation, abusing an unsafe deserialization/file-inclusion path, or exploiting a file-upload handler that fails to neutralize.phpextensions. - Command execution: The attacker runs low-and-slow recon:
whoami,hostname,net user,ipconfig /all(Windows) orid,uname -a,cat /etc/passwd(Linux). The critical parent-child process relationship here is the web server or PHP binary spawning a shell — this is your highest-fidelity detection signal. - Web shell deployment: Attackers write a
.phppayload into a web-accessible directory — commonly upload folders,images/,tmp/,wp-content/uploads/, or theme directories. Filenames are often randomized or masquerade as legitimate files (sys.php,cache_.php,up.php,404.php). - Persistence and pivoting: Scheduled tasks/cron entries, dropped tooling (C2 beacons, tunneling tools like frp or chisel), credential dumping, and lateral movement.
Exploitation Status
PHP flaws of this class are actively exploited in the wild — this is not a theoretical exercise. Historical precedent (mass exploitation of PHP-CGI and framework flaws within days of disclosure) plus current scanning telemetry means any unpatched, internet-facing PHP instance should be treated as a live target. Assume exploitation attempts have already reached your perimeter.
Assessing Your Real Risk
Before detection, answer these questions honestly:
- Is PHP reachable from the internet? Check load balancers, WAF rules, and CDN origins — not just what you think is exposed. Shadow IT and forgotten staging servers are where these attacks land.
- Which SAPI is in use? PHP-CGI on Windows carries specific argument-injection risk. PHP-FPM and mod_php have different exposure profiles. Run
php -vand inspect your web server handler configuration on every host. - Was there an exposure window? Compare your patch date against the disclosure date. Any gap of hours or days means hunt, don't assume.
- Are upload directories executable? If a folder that accepts user uploads also executes PHP, you have a structural web shell problem independent of any single CVE.
Detection & Hunting
The detections below target the two most reliable observables in PHP2Shell intrusions: (1) web server/PHP processes spawning shells and recon commands, and (2) new or modified PHP files appearing in web-accessible directories. These are high-fidelity signals — a legitimate php-cgi.exe essentially never spawns cmd.exe, and new .php files in upload directories are almost never benign.
Sigma Rules
---
title: PHP or Web Server Process Spawning Shell - Possible PHP2Shell Exploitation
id: 3f8a2c71-9b4e-4d5a-b6c7-8e1f2a3b4c5d
status: experimental
description: Detects php.exe, php-cgi.exe, httpd.exe, nginx.exe, or w3wp.exe spawning command shells or reconnaissance binaries, consistent with post-exploitation of PHP vulnerabilities and web shell activity.
references:
- https://www.darkreading.com/application-security/exposed-php2shell-cyber-experts-advice
- https://attack.mitre.org/techniques/T1505/003/
- https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.persistence
- attack.t1505.003
- attack.execution
- attack.t1059
logsource:
category: process_creation
product: windows
detection:
selection_parent:
ParentImage|endswith:
- '\php.exe'
- '\php-cgi.exe'
- '\httpd.exe'
- '\nginx.exe'
- '\w3wp.exe'
selection_child:
Image|endswith:
- '\cmd.exe'
- '\powershell.exe'
- '\pwsh.exe'
- '\whoami.exe'
- '\net.exe'
- '\net1.exe'
- '\ipconfig.exe'
- '\systeminfo.exe'
- '\nltest.exe'
- '\certutil.exe'
- '\bitsadmin.exe'
condition: selection_parent and selection_child
falsepositives:
- Rare; some PHP applications legitimately invoke system commands via exec()/shell_exec() (e.g., image processing, PDF tools). Baseline per-server and tune by ParentImage path.
level: high
---
title: Suspicious PHP Web Shell File Created in Web-Accessible Directory
id: 7c2d4e91-5a6b-4f8c-9d0e-1f2a3b4c5d6e
status: experimental
description: Detects creation of PHP files in upload, temp, cache, or image directories commonly abused for web shell placement following PHP exploitation.
references:
- https://www.darkreading.com/application-security/exposed-php2shell-cyber-experts-advice
- https://attack.mitre.org/techniques/T1505/003/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.persistence
- attack.t1505.003
logsource:
category: file_event
product: windows
detection:
selection_ext:
TargetFilename|endswith:
- '.php'
- '.phtml'
- '.phar'
- '.php7'
- '.php5'
selection_path:
TargetFilename|contains:
- '\uploads\'
- '\upload\'
- '\images\'
- '\img\'
- '\tmp\'
- '\temp\'
- '\cache\'
- '\files\'
- '\wp-content\uploads\'
condition: selection_ext and selection_path
falsepositives:
- Legitimate CMS plugin/theme updates and developer deployments. Correlate with deployment windows and known publisher processes; investigate any file written by the web server worker process itself.
level: high
---
title: PHP-CGI Invoked With Suspicious Command Line Arguments
id: 9e4f6a12-7b8c-4d0e-af12-3b4c5d6e7f8a
status: experimental
description: Detects php-cgi.exe executions with unusual command-line arguments (e.g., allow_url_include, auto_prepend_file, -d switches) indicative of argument-injection exploitation against PHP-CGI handlers.
references:
- https://www.darkreading.com/application-security/exposed-php2shell-cyber-experts-advice
- https://attack.mitre.org/techniques/T1190/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.initial_access
- attack.t1190
- attack.execution
logsource:
category: process_creation
product: windows
detection:
selection_img:
Image|endswith: '\php-cgi.exe'
selection_args:
CommandLine|contains:
- 'allow_url_include'
- 'auto_prepend_file'
- 'auto_append_file'
- '-d '
- 'data://'
- 'php://input'
- '%ad'
condition: selection_img and selection_args
falsepositives:
- Some hosting panels pass -d directives legitimately. Alert volume should be low on healthy servers; investigate any occurrence alongside HTTP access logs for the triggering request.
level: critical
KQL — Microsoft Sentinel / Defender
This query hunts the core post-exploitation behavior — web server and PHP processes spawning shells or recon tooling — and works whether your servers report via Defender for Endpoint or via Sysmon/Syslog ingestion:
let ReconBins = dynamic(["cmd.exe","powershell.exe","pwsh.exe","whoami.exe","net.exe","net1.exe","ipconfig.exe","systeminfo.exe","certutil.exe","bitsadmin.exe","sh","bash","curl","wget","id","uname"]);
let WebProcs = dynamic(["php.exe","php-cgi.exe","httpd.exe","nginx.exe","w3wp.exe","php-fpm","apache2"];
DeviceProcessEvents
| where TimeGenerated > ago(14d)
| where InitiatingProcessFileName in~ (WebProcs) or InitiatingProcessCommandLine has_any ("php-cgi","php-fpm")
| where FileName in~ (ReconBins)
| project TimeGenerated, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, AccountName, InitiatingProcessId, ProcessId
| order by TimeGenerated desc;
// Correlate with inbound HTTP requests hitting the same host (requires W3CIISLog or firewall/CEF ingestion)
CommonSecurityLog
| where TimeGenerated > ago(14d)
| where RequestURL has_any (".php","php-cgi") and RequestMethod == "POST"
| summarize RequestCount=count(), FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated) by SourceIP, RequestURL, DestinationHostName
| order by RequestCount desc;
Velociraptor VQL
Use this hunt artifact across your web server fleet to find both suspicious child processes and recently created PHP files in web roots — the two artifacts attackers almost always leave behind:
-- PHP2Shell hunt: web server processes spawning shells + recent PHP file drops in web roots
SELECT * FROM foreach(
row={
SELECT Pid, Ppid, Name, Exe, CommandLine, Username, CreateTime
FROM pslist()
WHERE (Exe =~ '(?i)(php|php-cgi|httpd|nginx|w3wp|apache)'
AND CommandLine =~ '(?i)(cmd\.exe|powershell|whoami|/bin/(sh|bash)|certutil|curl|wget)')
OR (Name =~ '(?i)^(cmd|powershell|whoami|sh|bash)'
AND CommandLine =~ '(?i)(php|www-data|apache|nginx|iis)')
},
query={
SELECT Pid, Ppid, Name, Exe, CommandLine, Username, CreateTime,
'suspicious_process' AS Finding
FROM scope()
})
-- Separately: enumerate recently modified PHP files in common web roots
SELECT FullPath, Size, Mtime, Btime,
'recent_php_in_webroot' AS Finding
FROM glob(globs=[
'C:/inetpub/wwwroot/**/*.php',
'C:/xampp/htdocs/**/*.php',
'/var/www/**/*.php',
'/srv/www/**/*.php',
'/home/*/public_html/**/*.php'
])
WHERE Mtime > now() - 86400*7
AND FullPath =~ '(?i)(upload|images|tmp|temp|cache|files)'
ORDER BY Mtime DESC
Remediation & Verification Script
Run this on Windows-based PHP servers (IIS, Apache, XAMPP — the stacks most commonly hit by PHP-CGI exploitation) to inventory PHP versions, flag end-of-life branches, and sweep for recently created PHP files in web roots:
# PHP2Shell exposure assessment and web shell sweep - run elevated on each web server
# 1. Locate and version every PHP / PHP-CGI binary on the host
$phpPaths = @("C:\php","C:\xampp\php","C:\Program Files\PHP","C:\inetpub\wwwroot")
foreach ($p in $phpPaths) {
if (Test-Path $p) {
Get-ChildItem -Path $p -Recurse -Include php.exe,php-cgi.exe -ErrorAction SilentlyContinue |
ForEach-Object {
$v = & $_.FullName -v 2>$null | Select-Object -First 1
Write-Host "[PHP BINARY] $($_.FullName) -> $v"
if ($v -match 'PHP (5\.|7\.|8\.0\.|8\.1\.)') {
Write-Warning "END-OF-LIFE or unsupported PHP branch detected: $v - upgrade required"
}
}
}
}
# 2. Check whether php-cgi is exposed via IIS handler mappings
$handlers = & "$env:SystemRoot\System32\inetsrv\appcmd.exe" list config -section:handlers 2>$null
if ($handlers -match 'php-cgi') { Write-Warning "IIS handler maps PHP requests to php-cgi.exe - verify patch level and request filtering" }
# 3. Sweep web roots for PHP files created/modified in the last 14 days
$webRoots = @("C:\inetpub\wwwroot","C:\xampp\htdocs")
foreach ($root in $webRoots) {
if (Test-Path $root) {
Get-ChildItem -Path $root -Recurse -Include *.php,*.phtml,*.phar -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-14) } |
Select-Object FullName, CreationTime, LastWriteTime |
Format-Table -AutoSize
}
}
# 4. Flag PHP processes with shell children still running right now
Get-CimInstance Win32_Process |
Where-Object { $_.Name -match 'cmd.exe|powershell.exe' } |
ForEach-Object {
$parent = Get-CimInstance Win32_Process -Filter "ProcessId=$($_.ParentProcessId)" -ErrorAction SilentlyContinue
if ($parent.Name -match 'php|httpd|nginx|w3wp') {
Write-Warning "ACTIVE: $($parent.Name) (PID $($parent.ProcessId)) spawned $($_.Name) (PID $($_.ProcessId)) -> $($_.CommandLine)"
}
}
Remediation Steps
-
Patch immediately — then verify. Identify the exact PHP build on every server (
php -v), including containers, vendor appliances, and bundled stacks like XAMPP/WampServer. Upgrade to the latest supported 8.x release published in the official PHP security advisories and confirm the running binary actually changed — a shocking number of "patched" servers still run the old binary from a stale path or an unrestarted PHP-FPM service. Restart web services and re-verify. -
Assume compromise for any exposure window. If the server was internet-facing and unpatched after public disclosure, run the hunts above before declaring the incident closed. Look specifically for web shells in upload directories, unexpected child processes, and new local accounts or scheduled tasks/cron jobs.
-
Disable execution in upload directories. Add web server configuration to prevent PHP parsing in any directory that accepts user content. In Apache, use
php_flag engine offin the directory context; in nginx/PHP-FPM, omitlocation ~ \.php$matching for upload paths; in IIS, remove script handler mappings from upload folders. -
Retire PHP-CGI where possible. Move Windows PHP workloads to a supported handler model, and if PHP-CGI must remain, apply strict request filtering (URLScan/IIS Request Filtering or WAF rules) to reject requests containing suspicious query patterns such as
-%s,-d, and encoded argument-injection sequences. -
Put a WAF in front of PHP origins. Virtual patching buys time during disclosure windows. Ensure your WAF is blocking known PHP exploitation patterns and, critically, that it sees decrypted traffic to the true origin — CDN bypass via direct-origin IP is a common attacker move.
-
Reduce the attack surface. Remove phpMyAdmin and admin panels from public exposure, disable dangerous functions (
exec,shell_exec,system,passthru) inphp.iniviadisable_functionswhere the application allows, setallow_url_include = Offandexpose_php = Off, and enforceopen_basedirrestrictions. -
Instrument for the next time. Ensure process creation logging (Sysmon with command-line capture, or Defender for Endpoint) is deployed on every web server, that web access logs are centralized and retained, and that file integrity monitoring covers web roots. The dwell time on web shell intrusions is measured in months when nobody is watching.
-
Inventory your PHP estate. You cannot patch what you don't know about. Build and maintain a definitive list of every PHP runtime — including the ones embedded in third-party appliances and developer workstations — so the next critical PHP advisory is a targeted operation, not a scavenger hunt.
Related Resources
Security Arsenal Healthcare Cybersecurity AlertMonitor Platform Book a SOC Assessment healthcare Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.