What Is php2shell and How Do You Mitigate It?
php2shell is the community name for a family of pre-authenticated
PHP remote code execution exploits built on
session.upload_progress and the rfc1867 multipart parser
(publicly tracked as CVE-2026-109618 and CVE-2026-109619).
A single crafted multipart upload can race PHP's upload-progress bookkeeping, free
session memory that the parser still references, and then reuse that memory with
attacker-controlled PHPSESSID cookies to poison a session file — which is
later included through an LFI sink to run arbitrary code as www-data.
Related chains abuse serialized-object mismatches, Apache/PHP-FPM worker pinning for an
ASLR bypass, and SAPI out-of-memory bailouts; the PHP-CGI argument-injection bug
(CVE-2024-4577) is blocked by the same agent.
PHP2Shell Sentinel is a free, open-source PHP exploit
mitigation tool — a single static Go binary you run on the PHP host or in
front of it as a PHP WAF. It blocks upload-progress probes before the
PHP parser sees them, kills poisoned sess_* files, terminates shells
spawned by compromised workers, hardens php.ini, and quarantines attacking
IPs at the OS firewall. It is a zero-dependency alternative to writing custom
ModSecurity or Nginx rules for these chains, works on PHP 5.4–8.5,
and needs no application code changes.
- Targets:
session.upload_progressUAF (CVE-2026-109618 / CVE-2026-109619), session-file poisoning & LFI-to-RCE chains, serialized handler mismatch, worker-pinning ASLR bypass, SAPI OOM bailout, and PHP-CGI injection (CVE-2024-4577). - Platforms: Linux (systemd, iptables,
/proc, journald) and Windows (SCM, netsh, Toolhelp32, Event Log) — including XAMPP, WAMP, Laragon and IIS. - Deployment: out-of-band host agent by default, optional in-line HTTP reverse-proxy shield; no port or application changes required.
www-data without Sentinel, and is
rejected with HTTP 403 (rule P2S-2026100) through Sentinel. Recorded against the
reproducible lab in the repository.
Quick Install (Linux & Windows)
proxy_enabled: true and a
real backend_url, then route public traffic to
listen_addr. Never enable the proxy against a placeholder
backend — clients will receive 502.
Option A: One-Line Installer (Downloads binary, audits PHP, installs & starts systemd service)
curl -fsSL "https://suleman-elahi.github.io/php2shell-sentinel/install.sh" | sudo sh
Option B: Step-by-Step Binary Download via curl
# 1. Download static Linux binary curl -fsSL "https://suleman-elahi.github.io/php2shell-sentinel/downloads/sentinel-linux-amd64" -o sentinel chmod +x sentinel sudo mv sentinel /usr/local/bin/sentinel # 2. Audit PHP installation & apply php.ini virtual patches sentinel check sudo sentinel harden # 3. Install & start as a persistent systemd service # (out-of-band guards by default; no web port changes) sudo sentinel install sudo sentinel start # 4. Optional in-line WAF: set proxy_enabled=true, listen_addr=127.0.0.1:8080, # and backend_url=http://127.0.0.1:<real-php-port> in # /etc/php2shell-sentinel/sentinel.json, then restart: sudo systemctl restart php2shell-sentinel
Option A: One-Line PowerShell Installer (Run in Elevated Administrator PowerShell)
irm "https://suleman-elahi.github.io/php2shell-sentinel/install.ps1" | iex
Option B: Step-by-Step Download & Windows Service Setup
# 1. Download static Windows binary (Run in Elevated PowerShell) New-Item -ItemType Directory -Force -Path "C:\Program Files\PHP2ShellSentinel" | Out-Null Invoke-WebRequest -Uri "https://suleman-elahi.github.io/php2shell-sentinel/downloads/sentinel-windows-amd64.exe" -OutFile "C:\Program Files\PHP2ShellSentinel\sentinel.exe" # 2. Audit PHP installation & apply php.ini virtual patches & "C:\Program Files\PHP2ShellSentinel\sentinel.exe" check & "C:\Program Files\PHP2ShellSentinel\sentinel.exe" harden # 3. Install & start as a Windows Background Service (Auto-Start via SCM) # (out-of-band guards by default; no web port changes) & "C:\Program Files\PHP2ShellSentinel\sentinel.exe" install & "C:\Program Files\PHP2ShellSentinel\sentinel.exe" start # 4. Optional in-line WAF: set proxy_enabled=true, listen_addr, # and backend_url in C:\ProgramData\php2shell-sentinel\sentinel.json, # then restart the service.
Deployment Modes
Choose out-of-band host protection, in-line HTTP inspection, or both. The proxy is intentionally disabled by default so a fresh install cannot break a host whose PHP backend is not on a guessed address.
| Mode | How to run | Protects | Traffic change |
|---|---|---|---|
| Out-of-band (default) | sentinel install / sentinel run |
Session poisoning, spawned shells, runaway workers, crash/OOM log signals, php.ini hardening, optional firewall bans | None |
| In-line WAF | sentinel run -proxy -listen 127.0.0.1:8080 -backend http://127.0.0.1:8081 |
All out-of-band protections plus pre-parser blocking of multipart, request, cookie, response, and scanner vectors | Route public traffic to listen_addr; keep the PHP backend private |
Enable the in-line shield
Point backend_url at the real PHP listener (FPM, Apache, nginx,
or IIS), then route public traffic to listen_addr.
# /etc/php2shell-sentinel/sentinel.json "proxy_enabled": true, "listen_addr": "127.0.0.1:8080", "backend_url": "http://127.0.0.1:8081" # restart, then confirm the backend is reachable sudo systemctl restart php2shell-sentinel sudo journalctl -u php2shell-sentinel -n 20 curl -s http://127.0.0.1:9095/status
If the backend is unreachable, Sentinel logs
backend_unreachable and the proxy returns
502. Fix backend_url, or run with
-no-proxy if only out-of-band protection is intended.
Direct Downloads
sentinel-linux-amd64
Static Linux x86_64 binary (CGO_ENABLED=0). Includes WAF proxy, /proc watchdog, session guard, and systemd manager.
sentinel-windows-amd64.exe
Static Windows x86_64 executable. Native Windows Service Control Manager (SCM) & Win32 Toolhelp32 process watchdog.
Configuration & Checksums
Default JSON configuration template (out-of-band by default; enable the proxy explicitly), installer scripts, and SHA-256 checksums for binary verification.
Verified Results (2026-10-23)
Tested against public php2shell PoCs and a deliberately vulnerable PHP 8.2.34-FPM + nginx lab: first exploitability without Sentinel, then the same requests through Sentinel.
| Check | Result |
|---|---|
Go unit tests (go test ./...) |
PASS — all packages |
| Repository safe lab (harmless replay) | PASS — 403 P2S-2026100 |
| Public Chain A RCE, no Sentinel | RCE CONFIRMED — uid=33(www-data) |
| Public Chain A RCE through Sentinel | BLOCKED — 403 P2S-2026100, no command execution |
php2shell-scanner, no Sentinel / through Sentinel |
VULNERABLE / BLOCKED — 403 P2S-2026107-SCAN |
| Rule-by-rule WAF matrix | 19/19 PASS |
| Process watchdog | PASS — killed long-lived shell children, P2S-PROC-EXEC |
| Session-file guard | PASS — removed/quarantined poisoned sess_* files |
| php.ini hardener | PASS — upload_progress=Off, use_only_cookies=On, backup created |
| Exact binary as in-line WAF | PASS — clean 200; Chain A 403 |
Limitations: normal process sampling runs every 150ms;
a malicious WAF finding triggers a 10ms burst for 5s so short-lived
system('id') children are far more likely to be caught.
Common PHP version strings are scrubbed from small, uncompressed
text/JSON/XML responses (scrub_php_version_in_body), but
large, compressed, or non-text output is still application-owned.
A scanner may report INCONCLUSIVE when its probe is
rejected with 403; that is expected — the probe never
reached PHP. The 3-probe/60s quarantine is configurable
(probe_threshold, probe_window). Sentinel is
mitigation, not a substitute for patching PHP and disabling
session.upload_progress.enabled.
What Works with "Zero Application Modifications"
Sentinel can run as an in-line HTTP reverse proxy shield or completely out-of-band (-no-proxy -firewall) without altering existing Apache, Nginx, IIS, or PHP ports.
| Capability | How Sentinel Does It Passively | App Changes? |
|---|---|---|
| Blocking Spawned Shells | Scans OS process tree (/proc on Linux, Win32 CreateToolhelp32Snapshot on Windows) every 150ms, with a 10ms burst for 5s after a WAF finding and kills shells (sh, bash, cmd.exe, powershell.exe, nc, curl) spawned by PHP workers. |
No |
| Deleting Poisoned Sessions | Watches /var/lib/php/sessions, /tmp, C:\Windows\Temp, and C:\xampp\tmp for sess_* files containing <?php, upload_progress_ state, |O:/|C: object gadgets, or > 4 MiB inflation and quarantines/deletes them immediately. Legitimate upload_progress_...|a: arrays are cleaned as upload state, not mislabelled as handler mismatch. |
No |
| Detecting Crashes & OOM Bailouts | Reads directly from journalctl (Linux), Windows Application Event Log (wevtutil), and web error logs for in Unknown on line 0, Session object destruction failed, and SIGSEGV. |
No |
| Quarantining Attacking IPs | Automatically inserts DROP/Block rules into the OS firewall (iptables on Linux, netsh advfirewall on Windows). |
No |
| Deep Multipart UAF Prevention | When run in-line, aborts PHP_SESSION_UPLOAD_PROGRESS, blocks two-phase stalled uploads (< 900ms chunk timeout), caps per-IP concurrent multipart connections (defeating 149-worker pinning), and scrubs Set-Cookie: PHPSESSID heap leaks. |
Point HTTP backend |
CLI & Service Operations Reference
Linux Commands
# Audit local PHP version & php.ini files (read-only) sentinel check # Apply virtual patches (session.upload_progress.enabled = Off) with .bak backup sudo sentinel harden # Run in Zero-Touch Out-of-Band Mode (watches /proc, sessions, journald & iptables) sudo sentinel run -no-proxy -firewall -log-file /var/log/php2shell-sentinel.log # Check systemd service status, live telemetry & journal logs sudo systemctl status php2shell-sentinel sudo journalctl -u php2shell-sentinel -f sentinel status
Windows Commands (Elevated PowerShell / Administrator CMD)
# Audit local Windows PHP installation (XAMPP / WAMP / Laragon / IIS) sentinel.exe check -path C:\xampp\php # Apply php.ini virtual patches with automatic .bak backup sentinel.exe harden -path C:\xampp\php\php.ini # Run in Zero-Touch Out-of-Band Mode on Windows (Win32 processes, sess_*, Event Log, Windows Firewall) sentinel.exe run -no-proxy -firewall -log-file C:\ProgramData\php2shell-sentinel\sentinel.log # Query Windows Service status, live telemetry, and Windows Firewall rules Get-Service PHP2ShellSentinel sc.exe query PHP2ShellSentinel sentinel.exe status netsh advfirewall firewall show rule name=all | findstr PHP2ShellSentinel
Built-In Detection & Mitigation Rules
| Rule ID | CVE / Chain | Target Vector & Mitigation |
|---|---|---|
P2S-2026100 |
CVE-2026-109618 / 109619 | Blocks or strips PHP_SESSION_UPLOAD_PROGRESS in POST/PUT/PATCH multipart bodies (including multipart/form-data,boundary=x). |
P2S-2026100-STALL |
CVE-2026-109618 / 109619 | Aborts stalled multipart uploads (> 900ms inter-chunk pause) before php_session_track_init() frees arData. |
P2S-2026100-STREAM |
CVE-2026-109618 / 109619 | Sliding-window scanning of large multipart uploads with pooled buffers, preventing padding bypasses before PHP parses the body. |
P2S-2026101 |
PR #22113 | Blocks name="PHPSESSID" in multipart request bodies (prevents session.use_only_cookies bypass). |
P2S-2026102 |
Chain A (Session LFI) | Blocks PHP tags (<?php, <?=) inside multipart upload filenames. |
P2S-2026103 |
Chain C (Handler Mismatch) | Blocks |O:... / |C:... serialized object injection inside upload filenames. Plain serialized arrays (|a:) used by upload-progress state are not treated as object gadgets. |
P2S-2026104 |
Chain E & Heap Spray | Blocks upload filenames > 200 bytes (stops 231B ZendMM 256B bin spray and 10KB+ session inflation). |
P2S-2026105 |
Chain D (SAPI OOM) | Blocks Transfer-Encoding: chunked on multipart uploads (post_max_size bypass) and excessive part counts. |
P2S-2026106 |
Chain A (LFI / pearcmd) | Blocks LFI paths referencing sess_* or pearcmd.php (+config-create+). |
P2S-2026107 / 108 |
Scanner / Heap Shape | Blocks a[][a][a]... nested bracket probes and empty-boundary discrepancy probes; auto-bans after 3 probes/60s (configurable). |
P2S-2026109 / 111 / 112 |
Pure HTTP ASLR Bypass & Response Guard | Blocks forged binary Cookie: PHPSESSID pointers, scrubs 800KB Set-Cookie: PHPSESSID heap leaks, and quarantines Session object destruction failed / PHP OOM responses. |
P2S-2026116 |
Version Disclosure Scrub | Replaces PHP 8.x.y / PHP Version 8.x.y strings in small, uncompressed text, JSON, and XML responses and fixes Content-Length. |
P2S-20244577 |
CVE-2024-4577 | Blocks PHP-CGI soft-hyphen (%ADd) and -d allow_url_include / auto_prepend_file argument injection. |
Frequently Asked Questions
What is php2shell?
php2shell is the community name for a family of pre-authenticated
PHP remote code execution exploits built on
session.upload_progress and the rfc1867 multipart parser.
A crafted multipart upload races PHP's upload-progress bookkeeping to trigger a
use-after-free, poisons a session file (typically via a forged
PHPSESSID), and includes it through an LFI sink to run code as
www-data. Public PoCs chain several additional stages — serialized
handler mismatch, worker pinning for an ASLR bypass, and SAPI OOM bailouts.
Which CVEs does PHP2Shell Sentinel mitigate?
CVE-2026-109618 and CVE-2026-109619 (the
session.upload_progress / rfc1867 UAF chains) and
CVE-2024-4577 (PHP-CGI argument injection on Windows and Linux).
It also blocks the LFI-to-RCE, serialized-object injection, ASLR bypass and
OOM-bailout stages used by public PoCs.
How do I mitigate a PHP session.upload_progress RCE without patching PHP?
Set session.upload_progress.enabled = Off, upgrade PHP to a fixed
release, and run a compensating control. Sentinel blocks the upload-progress field,
aborts stalled multipart uploads (sub-second inter-chunk deadline), cleans poisoned
session files, and quarantines attacking IPs — all without changing application code.
Does it require application or framework changes?
No. Sentinel runs as a host agent out-of-band by default (session guard, process
watchdog, log tailer, php.ini hardening, optional firewall bans) and can be enabled
as an in-line HTTP reverse-proxy shield by pointing backend_url at your
existing PHP listener. Apache, nginx, IIS, PHP-FPM, XAMPP, WAMP and Laragon layouts
are supported on Linux and Windows.
Is PHP2Shell Sentinel free and open source?
Yes — MIT licensed, written in Go, shipped as a single static binary with no runtime dependencies, and published with SHA-256 checksums for download verification.
Is Sentinel a replacement for patching PHP?
No — it is mitigation. Keep session.upload_progress.enabled = Off,
disable display_errors in production, and upgrade to a fixed PHP release
(8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 or newer for the affected lines). Sentinel buys
time and blocks exploitation attempts in the meantime.
How can I verify that it actually blocks the exploit?
The project publishes verification results: a public Chain A PoC that achieves
uid=33(www-data) on a vulnerable PHP 8.2.34-FPM lab is blocked with
HTTP 403 (rule P2S-2026100) when routed through Sentinel,
a 19-rule WAF matrix passes, and scanner probes are rejected before reaching the PHP
parser. See Verified Results and the
GitHub repository.