Back to Intelligence

PHP2Shell: Emergency PHP Patches Fix Two Critical Pre-Auth RCE Flaws — Detection and Remediation Guide

SA
Security Arsenal Team
October 21, 2026
13 min read

The PHP project has shipped out-of-band emergency releases — PHP 8.5.12, 8.4.27, 8.3.36, and 8.2.35 — to remediate two critical code execution flaws collectively dubbed PHP2Shell. Both flaws allow an unauthenticated remote attacker to execute arbitrary code on the web server, and both live in the PHP runtime itself, not in any application, framework, or CMS. That distinction matters: no amount of secure coding in your WordPress, Laravel, or custom application layer mitigates a vulnerability in the interpreter parsing the request. According to reporting on the disclosure, exploitation attempts began surfacing shortly after the patches dropped, and the PHP team's guidance is unambiguous — fully updating is the only complete protection.

If you operate any PHP-facing surface — php-fpm behind Nginx or Apache, mod_php, php-cgi on IIS, containerized workloads, or appliances with embedded PHP — treat this as a patch-now event.

Why PHP2Shell Is Different From a Typical Web Vulnerability

Most web RCEs we respond to are application-layer: a deserialization bug in a plugin, an injection flaw in a framework, a file upload bypass in a CMS. Those are bad, but they're scoped — one application, one endpoint, one code path.

PHP2Shell sits underneath all of that. The vulnerable component is the runtime — the engine every PHP request passes through. The defensive implications are significant:

  • Application code review is irrelevant. A perfectly hardened application is still exposed because the flaw is triggered before or independent of application logic.
  • Every SAPI is potentially exposed. php-fpm, mod_php, php-cgi, and embedded PHP in third-party products all load the same interpreter core.
  • No authentication barrier. Pre-auth exploitation means internet-facing scanners can probe your entire fleet indiscriminately — and they are.
  • The blast radius is the web service account. Successful exploitation yields code execution as the PHP worker user (typically www-data, apache, nginx, or IIS AppPool identity) — a foothold that routinely escalates into webshell deployment, credential theft, and lateral movement.

Affected Versions and Exposure Surface

BranchVulnerableFixed ReleaseStatus
PHP 8.5.x< 8.5.128.5.12Patched
PHP 8.4.x< 8.4.278.4.27Patched
PHP 8.3.x< 8.3.368.3.36Patched
PHP 8.2.x< 8.2.358.2.35Patched
PHP ≤ 8.1AllNoneEnd of life — migrate branches

At the time of this writing, the public reporting on PHP2Shell does not include CVE assignments or CVSS scores; track the official PHP release announcements at php.net/downloads.php and the php/php-src GitHub security advisories for CVE identifiers as they're published. Do not wait for a CVE number to patch — the vendor has already told you the severity with an emergency release cadence.

Don't forget the hidden PHP. In our IR engagements, the servers that stay vulnerable longest are the ones nobody knew ran PHP: network management appliances, monitoring dashboards, ticketing systems, backup consoles, CI/CD tooling, and Docker images built months ago from a pinned base. Your vulnerability scanner's asset inventory is the floor here, not the ceiling. Query your CMDB, your container registry, and your EDR software inventory for anything with php, php-fpm, or php-cgi binaries.

How the Attack Works — A Defender's View

The attack chain for a runtime-level pre-auth RCE is short, which is exactly the problem:

  1. Delivery: A crafted HTTP request reaches any PHP-handled endpoint. Because the flaw is in the runtime, the attacker does not need a specific vulnerable page — any request the interpreter processes is a potential trigger surface.
  2. Trigger: The malformed input reaches the vulnerable code path in the PHP engine during request handling.
  3. Execution: Arbitrary commands run in the context of the PHP worker process — no credentials, no session, no user interaction.
  4. Post-exploitation: From dozens of webshell and RCE engagements, the follow-on behavior is highly predictable: the attacker spawns a system shell (/bin/sh, bash, cmd.exe), retrieves a second-stage payload (curl/wget to an external host), drops a webshell into the document root or a writable temp path (/tmp, /dev/shm, /var/tmp, uploads/), and establishes persistence.

This predictability is your advantage. You may not be able to signature the exploit request itself (the exact trigger patterns are not yet public), but post-exploitation process lineage is loud. A PHP worker process spawning bash or cmd.exe is almost never legitimate in a well-run production environment.

Exploitation Status

  • Exploitation attempts: Emerging in the wild, per reporting on the disclosure. Expect internet-wide scanning to industrialize within days — that is the consistent pattern for pre-auth runtime flaws in ubiquitous software.
  • Public PoC: Not confirmed in available reporting at publication time. Assume one will surface; the window between emergency patch and weaponized PoC for PHP-class bugs is historically measured in days, not weeks.
  • CISA KEV: Not listed as of this writing. Monitor the CISA KEV catalog — federal deadlines for PHP2Shell could follow quickly if exploitation scales.

Treat the absence of confirmed mass exploitation as an opportunity to patch ahead of the curve, not as reassurance.

Detection and Response

The rules below target the highest-fidelity, lowest-noise observables: PHP and web server processes doing things PHP workers should never do. Deploy them even after patching — they will catch exploitation of the next runtime bug too, and they'll catch the webshell dropped last month that you don't know about yet.

Sigma Detection Rules

YAML
---
title: PHP or Web Server Process Spawning System Shell on Linux
id: 3f7b1a2e-9c4d-4e8f-a1b2-6d5c7e8f9a01
status: experimental
description: Detects PHP runtime or web server processes spawning a system shell, consistent with PHP2Shell post-exploitation behavior where code execution lands in the PHP worker context.
references:
  - https://thehackernews.com/2026/10/php-issues-emergency-patches-for-two.html
  - https://attack.mitre.org/techniques/T1190/
  - https://attack.mitre.org/techniques/T1059/004/
author: Security Arsenal
date: 2026/10/09
tags:
  - attack.initial_access
  - attack.t1190
  - attack.execution
  - attack.t1059.004
logsource:
  category: process_creation
  product: linux
detection:
  selection_parent:
    ParentImage|endswith:
      - '/php-fpm'
      - '/php-cgi'
      - '/php'
      - '/apache2'
      - '/httpd'
      - '/nginx'
  selection_child:
    Image|endswith:
      - '/sh'
      - '/bash'
      - '/dash'
      - '/zsh'
      - '/ash'
  condition: selection_parent and selection_child
falsepositives:
  - Application code legitimately invoking shell via exec(), system(), or shell_exec() — inventory these and suppress per-host
  - Web-triggered maintenance or deployment scripts
level: high
---
title: PHP Worker Executing Payload Retrieval or Tunneling Tools
id: 8c2e4d61-7b3a-4f19-9c85-2a1b6d4e7f02
status: experimental
description: Detects PHP or web server child processes invoking download, tunneling, or encoding utilities — the classic second-stage retrieval pattern following runtime RCE exploitation such as PHP2Shell.
references:
  - https://thehackernews.com/2026/10/php-issues-emergency-patches-for-two.html
  - https://attack.mitre.org/techniques/T1105/
  - https://attack.mitre.org/techniques/T1071/001/
author: Security Arsenal
date: 2026/10/09
tags:
  - attack.command_and_control
  - attack.t1105
  - attack.t1071.001
logsource:
  category: process_creation
  product: linux
detection:
  selection_parent:
    ParentImage|endswith:
      - '/php-fpm'
      - '/php-cgi'
      - '/php'
      - '/apache2'
      - '/httpd'
      - '/nginx'
  selection_child:
    Image|endswith:
      - '/curl'
      - '/wget'
      - '/nc'
      - '/ncat'
      - '/socat'
      - '/base64'
      - '/perl'
      - '/python'
      - '/python3'
  condition: selection_parent and selection_child
falsepositives:
  - Health-check or integration code shelling out to curl from PHP applications — tune per application
level: high
---
title: PHP CGI Spawning Command Shell on Windows
id: 5d1a9f37-4e62-4c08-b743-9f2c8a5e6103
status: experimental
description: Detects php-cgi.exe, php.exe, or Apache on Windows spawning command interpreters or script engines, consistent with PHP2Shell exploitation against IIS/FastCGI or Apache Windows deployments.
references:
  - https://thehackernews.com/2026/10/php-issues-emergency-patches-for-two.html
  - https://attack.mitre.org/techniques/T1190/
  - https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/10/09
tags:
  - attack.initial_access
  - attack.t1190
  - attack.execution
  - attack.t1059
logsource:
  category: process_creation
  product: windows
detection:
  selection_parent:
    ParentImage|endswith:
      - '\php-cgi.exe'
      - '\php.exe'
      - '\httpd.exe'
  selection_child:
    Image|endswith:
      - '\cmd.exe'
      - '\powershell.exe'
      - '\pwsh.exe'
      - '\wscript.exe'
      - '\cscript.exe'
      - '\mshta.exe'
      - '\rundll32.exe'
  condition: selection_parent and selection_child
falsepositives:
  - PHP applications invoking system commands via exec() for legitimate business logic
level: high

KQL Hunting — Microsoft Sentinel / Defender

These queries assume Linux web servers are forwarding process telemetry via auditd/Syslog or are onboarded to Defender for Endpoint, and that Windows IIS/PHP hosts report DeviceProcessEvents.

KQL — Microsoft Sentinel / Defender
// Hunt 1: PHP/web server processes spawning shells, downloaders, or tunneling tools (last 7 days)
let suspiciousChildren = dynamic(["sh", "bash", "dash", "zsh", "curl", "wget", "nc", "ncat", "socat", "base64", "perl", "python", "python3", "cmd.exe", "powershell.exe", "pwsh.exe", "mshta.exe"]);
DeviceProcessEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName has_any ("php", "php-fpm", "php-cgi", "apache2", "httpd", "nginx", "w3wp.exe")
| where FileName in~ (suspiciousChildren)
   or ProcessCommandLine has_any ("bash -i", "/dev/tcp/", "nc -e", "ncat -e", "| sh", "| bash", "base64 -d")
| project TimeGenerated, DeviceName, AccountName, InitiatingProcessFileName, InitiatingProcessCommandLine, FileName, ProcessCommandLine, SHA256, RemoteIP
| order by TimeGenerated desc;

// Hunt 2: Linux hosts forwarding auditd/execve telemetry via Syslog — PHP worker spawning suspicious children
Syslog
| where TimeGenerated > ago(7d)
| where SyslogMessage has_any ("php-fpm", "php-cgi")
| where SyslogMessage has_any ("/bin/sh", "/bin/bash", "curl ", "wget ", "nc ", "ncat ", "socat", "base64 -d", "/dev/tcp/")
| project TimeGenerated, Computer, ProcessName, SyslogMessage
| order by TimeGenerated desc;

// Hunt 3: Outbound connections from PHP/web server processes to non-standard destinations — payload staging or reverse shell C2
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName has_any ("php", "php-fpm", "php-cgi", "curl", "wget", "nc", "ncat", "socat", "bash", "sh")
| where InitiatingProcessAccountName has_any ("www-data", "apache", "nginx", "nobody")
| where RemoteIPType == "Public" and RemotePort !in (80, 443)
| summarize ConnectionCount = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteIP, RemotePort
| order by ConnectionCount asc;

Hunt 3 is sorted ascending deliberately: a single connection from www-data to a public IP on port 4444 is far more interesting than ten thousand to a CDN.

Velociraptor VQL Hunt

Use this as an on-demand artifact against suspected Linux web servers to enumerate PHP lineage, confirm the runtime version, and surface recently dropped web-executable files (webshell staging).

VQL — Velociraptor
-- PHP2Shell hunt: PHP/web process lineage, suspicious child execution, runtime version, and dropped web files

-- Part 1: PHP and web server processes with suspicious command lines or children
SELECT Pid, Ppid, Name, CommandLine, Exe, Username, CreateTime
FROM pslist()
WHERE Name =~ '(?i)php|httpd|apache|nginx'
   OR CommandLine =~ '(?i)/bin/(ba)?sh|curl |wget |nc |ncat |socat |base64 -d|/dev/tcp/'

-- Part 2: Confirm the running PHP version against the patched floors (8.5.12 / 8.4.27 / 8.3.36 / 8.2.35)
SELECT Stdout
FROM execve(argv=['php', '-v'])

-- Part 3: Recently created PHP files in web roots and writable temp paths (potential dropped webshells, last 72 hours)
SELECT FullPath, Size, Mtime, Ctime
FROM glob(globs=['/var/www/**/*.php', '/srv/www/**/*.php', '/tmp/*.php', '/tmp/**/*.php', '/var/tmp/*.php', '/dev/shm/*'])
WHERE Mtime > now() - 259200
ORDER BY Mtime DESC

Any hit on Part 1 where Username is www-data and the command line contains a shell or /dev/tcp/ warrants immediate host isolation and a full memory acquisition — you are likely looking at live PHP2Shell post-exploitation.

Rapid Version Verification and Patch Script

Run this across your Linux fleet (via your configuration management or EDR live response) to identify vulnerable interpreters, upgrade through the package manager, restart the SAPIs so the patched runtime is actually loaded, and sweep for recently dropped web files.

Bash / Shell
#!/usr/bin/env bash
# PHP2Shell emergency verification and remediation — Security Arsenal
# Patched floors: 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35. PHP <= 8.1 is EOL: migrate branches.

PHP_VERSION="$(php -v 2>/dev/null | head -n1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)"
echo "[*] Detected PHP CLI version: ${PHP_VERSION:-not found}"

is_patched() {
    local v="$1" major minor patch
    IFS='.' read -r major minor patch <<< "$v"
    case "$major.$minor" in
        8.5) [ "$patch" -ge 12 ] ;;
        8.4) [ "$patch" -ge 27 ] ;;
        8.3) [ "$patch" -ge 36 ] ;;
        8.2) [ "$patch" -ge 35 ] ;;
        *) return 1 ;;
    esac
}

if [ -n "$PHP_VERSION" ] && is_patched "$PHP_VERSION"; then
    echo "[+] PHP $PHP_VERSION meets the patched floor. Confirm fpm/mod_php/cgi SAPIs were restarted."
else
    echo "[!] VULNERABLE or unsupported PHP. Upgrading..."
    if command -v apt-get >/dev/null 2>&1; then
        sudo apt-get update && sudo apt-get install --only-upgrade -y 'php*'
    elif command -v dnf >/dev/null 2>&1; then
        sudo dnf upgrade -y 'php*'
    elif command -v yum >/dev/null 2>&1; then
        sudo yum update -y 'php*'
    elif command -v apk >/dev/null 2>&1; then
        sudo apk upgrade 'php*'
    else
        echo "[-] Unknown package manager. Patch manually from php.net or vendor repos."
    fi
fi

# Restart every PHP SAPI so the patched interpreter is loaded into memory
sudo systemctl restart php-fpm 2>/dev/null
for s in php8.2-fpm php8.3-fpm php8.4-fpm php8.5-fpm; do sudo systemctl restart "$s" 2>/dev/null; done
sudo systemctl restart apache2 httpd 2>/dev/null
sudo systemctl reload nginx 2>/dev/null

# Post-upgrade verification
echo "[*] Post-upgrade version:" && php -v | head -n1

# Sweep for recently dropped web-executable files (last 72h) — potential webshell staging
find /var/www /srv/www /tmp /var/tmp /dev/shm -type f -mmin -4320 \
    \( -name '*.php' -o -name '*.phtml' -o -name '*.phar' \) 2>/dev/null | head -n 50

# Audit: PHP processes with active outbound network connections right now
ss -tup 2>/dev/null | grep -Ei 'php|httpd|apache|nginx'

For Windows/IIS deployments, check Get-ChildItem 'C:\Program Files\PHP' and the FastCGI handler path in IIS, download the patched build from windows.php.net, replace the installation, and recycle the application pools. Verify with php-cgi.exe -v.

Remediation and Hardening

1. Patch immediately — this is the only complete fix.

  • PHP 8.5.x → 8.5.12
  • PHP 8.4.x → 8.4.27
  • PHP 8.3.x → 8.3.36
  • PHP 8.2.x → 8.2.35
  • PHP ≤ 8.1 → no fix is coming. These branches are end of life and will not receive the patch. Migrate to a supported branch now; an EOL interpreter with a known pre-auth RCE is an unacceptable exposure.

The PHP team has stated explicitly that only fully updating protects a server. Source releases and distribution packages are linked from php.net/downloads.php; confirm your distro (Debian, Ubuntu, RHEL/Rocky/Alma, Alpine) has shipped the backported fix rather than assuming version strings alone tell the story.

2. Restart every SAPI after patching. A patched binary on disk protects nothing if the old interpreter is still resident in memory. Restart php-fpm pools, Apache (mod_php), and recycle IIS application pools. Verify the loaded version, not just the installed one.

3. Inventory the full exposure. Containers and images (rebuild from updated base images — patching a running container is an anti-pattern), appliances with embedded PHP, hosting control panels, and development/staging servers that are internet-reachable. EDR software inventory plus container registry scanning will catch what the CMDB missed.

4. Compensating controls are scaffolding, not a fix. If you cannot patch a system this week:

  • Restrict network reachability — if a PHP surface does not need to be internet-facing, put it behind a VPN or allow-list today.
  • WAF/virtual patching can blunt generic probing, but without public trigger signatures for PHP2Shell, treat WAF coverage as partial at best.
  • Add temporary egress deny rules for the web service account (www-data should not be initiating outbound connections to arbitrary public IPs).
  • Disable dangerous functions where the application tolerates it: disable_functions = exec,passthru,shell_exec,system,proc_open,popen in php.ini raises the cost of post-exploitation even after a successful runtime exploit.
  • Run PHP workers with least privilege, read-only document roots where feasible, and open_basedir restrictions.

5. Hunt before you patch, not after. Exploitation attempts are already emerging, and runtime RCEs leave minimal request-log evidence. Run the Sigma/KQL/VQL content above across at least the last 7–14 days before you patch and reboot, so volatile evidence (process lineage, live connections) isn't destroyed. If you find post-exploitation indicators on an unpatched host, treat it as a confirmed intrusion: isolate, image, and investigate — do not simply patch and move on.

6. Verify from the outside. After patching, have your offensive team (or an external penetration test) validate that exposed PHP surfaces are actually running the patched runtime and that no residual webshells persist in writable paths. Patch-and-pray is how intrusions survive remediation.

Related Resources

Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.