Back Sploitus Exploit for Improperly Controlled Sequential Memory Allocation in Php
standard-library Python, tested end-to-end against a real `php:8.4.26-apache-bookworm`
> ⚠️ **For authorized security testing and research only.** Run against systems
> you own or have explicit written permission to test. The exploit tool crashes
> workers by design and can exhaust memory.
| **Lab** | ✅ running — `php2shell-lab` on ` (PHP 8.4.26, mod_php) |
| **Scanner** | ✅ v1.2 — version + SAPI + upload-progress fingerprinting, opt-in crash probe |
| **Exploit** | 🟡 tool complete — OOM producer + full A1→B1 chain verified; **leak/RCE not yet confirmed** (see [status](#exploitation-status)) |
Both bugs live in request startup, **before application code runs**, and both
stem from PHP's OOM bailout: an allocation exceeding `memory_limit` makes
`emalloc` fail → `zend_error_noreturn` → `zend_bailout()` → `longjmp` to the
request boundary, **skipping every cleanup step in between**.
| **CVE-2026-109618** (GHSA-w8hw-w9fx-j79w) | RCE via **OOM corruption** — half-initialized / freed-but-referenced structures survive the bailout; shutdown later reads attacker-shaped memory | 9.2 | 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 |
| **CVE-2026-109619** (GHSA-6wmf-4rv5-p3rp) | RCE via **stale global state** — the bailout skips a per-request global reset; dangling `zval*` survive into the request on the same worker → heap UAF / disclosure | 9.2 | 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 |
| **CVE-2026-115560** (Oct 23) | RCE via session upload progress — requires an unusual runtime switch of `session.serialize_handler`; informational advisory, fixed in the routine release | — | regular release |
- **Affected:** PHP 5.4 → 8.5.11 (all SAPIs serving web requests; **Apache mod_php = highest risk**, PHP-FPM / php-cgi / FrankenPHP / LiteSpeed also affected)
- **EOL:** PHP ≤ 8.1 — no official patch (some distros/LTS vendors backported)
- **Entry point:** `session.upload_progress` (default **on**). A multipart POST with a `PHP_SESSION_UPLOAD_PROGRESS` field writes a progress record into the session file **during request startup**; `session.use_strict_mode=0` lets the attacker pick the ID via the `PHPSESSID` POST field.
- **Heap pressure:** chunked / lengthless requests bypass `post_max_size`; hundreds of empty `filename=""` parts with nested names (`files[a][b][c]…`) build six `$_FILES` entries each and don't count toward `max_file_uploads` → a The image `php:8.4.27-apache-bookworm` (patched) is handy as a negative control.
> A bare image ships no PHP files — copy `version.php`/`upload.php` in for parity.
./scanner.py 127.0.0.1:8090 # → VULNERABLE (8.4.26, mod_php)
./scanner.py -f targets.txt --json --workers 16
./scanner.py example.com --no-active --verbose # passive fingerprint only
./scanner.py example.com --crash-probe # opt-in OOM probe (may trigger the bug!)
Verdicts: `VULNERABLE` · `LIKELY_VULNERABLE` · `LIKELY_PATCHED` · `EOL` · `UNKNOWN`.
Exit codes: `0` clean · `1` ≥1 vulnerable/likely/EOL · `2` no targets reachable · `3` usage error.
Detection: version fingerprint (`X-Powered-By`, `Server`, phpinfo heading, JSON
`php_version`), SAPI hints (Apache+PHP → mod_php; `Set-Cookie: PHPSESSID` as
PHP-presence evidence), and a graded upload-progress probe (an app echoing the
parsed `PHP_SESSION_UPLOAD_PROGRESS` field is a strong signal). The README
documents how CISA's SIGMA rule, Nuclei, Corelight, Assetnote, Wiz and httpx
detect PHP2Shell — and where each falls short.
python3 exploit.py check
python3 exploit.py offsets assets/libphp.so # → zval_ptr_dtor=0x64eb60 zif_system=0x45a3b0
# leak attempt (A1 OOM producer → B1 consumer → decode heap from response/cookie)
python3 exploit.py leak \
# RCE attempt — claim success only on proof
python3 exploit.py rce \
--cmd 'touch /tmp/pwned' --spray 500 --workers 12 --retries 1 --timeout 60
podman exec php2shell-lab ls -la /tmp/pwned
Use `--dry-run` to print the exact spray/gadget bytes without sending anything.
**Verified** (transcripts in [`exploit/tests/`](exploit/tests)):
1. **A1 OOM producer** — 1.06 MB chunked multipart (860 empty `filename=""`
nested parts + `PHP_SESSION_UPLOAD_PROGRESS`) reliably triggers
`PHP Fatal error: Allowed memory size of 134217728 bytes exhausted
(tried to allocate 8192 bytes) in Unknown on line 0` → HTTP 500 — the exact
pre-app-code window of CVE-2026-109618. The worker survives the bailout.
2. **Full A1→B1 chain** — the 33 MB B1 leak consumer executes cleanly; the
response carries a normal ~40 B `PHPSESSID` cookie (no heap payload yet).
3. **Ground truth** — host-side `/proc//mem` reads of worker `ps_globals`
work (validated non-zero reads); `PS(id)` is NULL in all idle workers.
**Not yet achieved:** heap leak / RCE. Open items (details in
[`exploit/README.md`](exploit/README.md#furthest-verified-phase--open-issues)):
1. **Worker affinity** — with a growing prefork pool, A1 and B1 may land on
different workers; correlate A1's PID from `podman logs` with B1's.
2. **`ps_globals` offsets** — the `id@+0x10` doc is unverified for this build;
3. **Heap-landing offset** — `s_offset=0xF8398` is uncalibrated; use
`rce --sweep` (bounded) once (1) and (2) are fixed.
The original unbounded recipe (`--workers 149`) pinned a growing prefork pool —
up to ~150 workers × 128 MB PHP heap ≈ 19 GB worst case — and OOM-killed the
--workers 12 --retries 1 --timeout 60 # wrap runs in `timeout(1)`
free -m # check before/after (≥ 2 GB available observed)
`--workers 12` = pool 11 + 1 free for the chain. Avoid unbounded
`/proc//mem` scans; the helper reads are 0x200 B per worker.
- [calif.io/php2shell]( — discovery writeup (request B = session ID + progress field, no file part, same worker)
- [TheCyberGeek/php2shell-research]( — five PoC chains, Docker labs
- [Wiz — PHP2Shell]( — `PHPSESSID` cookie carrying reclaimed heap
- [Assetnote/php2shell-scanner]( — differential empty-500 detection
- [Corelight/php2shell-detect]( — Zeek/Suricata signatures
- [CISA alert + SIGMA rule]( · [PHP Foundation](
- PHP security release: [8.5.12 / 8.4.27 / 8.3.36 / 8.2.35]( · GHSA-w8hw-w9fx-j79w · GHSA-6wmf-4rv5-p3rp
Vulnerabilities discovered by Wiz with Calif Research and OpenAI ("Patch the
Planet"). This repository is an independent lab/reproduction for defensive
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
