One-shot RCE proof of concept for the PHP `session.upload_progress` use-after-free vulnerabilities. The exploit chains a heap leak, a libphp leak, and a forged `pDestructor` call to reach `zif_system` — all over plain HTTP against a single `` page, with no authentication and no target-side code.
PHP 5.4.0 through 8.5.11 are affected (14 years of vulnerable code). PHP issued emergency patches on 2026-10-21. Use this only against systems you own or are authorized to test.
| Field | CVE-2026-109618 | CVE-2026-109619 |
|-------|-----------------|-----------------|
| Type | RCE via OOM memory corruption | RCE via stale global state after request |
| Root cause | `longjmp()` after OOM leaves `PS(http_session_vars)` dangling | Worker retains freed request memory across requests |
| Entry point | `session.upload_progress` multipart callback (before RINIT) | SAPI multipart parser / session handler |
| CVSS v4.0 | 9.2 Critical | 9.2 Critical |
| GHSA | [GHSA-w8hw-w9fx-j79w]( | [GHSA-6wmf-4rv5-p3rp]( |
All PHP versions with `session.upload_progress` support (introduced PHP 5.4.0, March 2012).
# Run (auto-derives libphp offsets from the container)
python3 php2shell.py exploit -u
[*] reading libphp offsets from container php2shell-target
[*] target
The default command is `id;hostname -I;hostname`. Commands longer than 14 bytes are automatically written to `out/c` and executed via `sh/out/p1'
Run `python3 php2shell.py --help` for all options. Use `--profile lab` for the original lab build (Apache 2.4.68); `--zif-offset` and `--dtor-offset` for non-default builds.
The target page is ``; `session_start()` is not called. All attack traffic is ordinary multipart POST:
0. **Worker rotation** — OOM + session init crash kills the prefork child; the replacement inherits the same address space layout.
1. **Heap leak** — `max_input_vars` warning sets `headers_sent=1`; `PHPSESSID=\x00` allocates a new session id with `send_cookie=1`; a 12 MB field OOMs inside `php_session_initialize()`, leaving `PS(http_session_vars)` dangling. A follow-up request reoccupies the freed reference with a multipart buffer, and `php_url_encode()` reads out of bounds into `Set-Cookie`, leaking Zend MM chunk pointers.
2. **libphp leak** — Similar technique places the dangling `PS(id)` so its `ZSTR_LEN` reads a HashTable's `nTableSize=16`; the data region exposes `pDestructor = ZVAL_PTR_DTOR = libphp+offset`, decoded from `Set-Cookie`.
3. **RCE** — A 167-byte surviving `zend_string` carries a fake HashTable with `pDestructor = zif_system`. A forged reference triggers the chain `php_session_track_init -> zval_ptr_dtor -> zend_array_destroy -> pDestructor(command)`.
curl -sI | grep X-Powered-By
Any version below the fix thresholds (8.2.35, 8.3.36, 8.4.27, 8.5.12) with `session.upload_progress.enabled = On` (the default) is exposed.
Update PHP to the fixed version for your branch and restart all workers (Apache, FPM, etc.):
# Change image tag from php:8.4.26-apache to php:8.4.27-apache (or later)
### Mitigate (if patching is not immediately possible)
These reduce attack surface but are not a substitute for patching.
For CVE-2026-109618 specifically, lowering `post_max_size` and raising `memory_limit` reduces the OOM surface:
On PHP 8.5, also disable fatal error backtraces:
Watch Apache/FPM error logs for unexpected child crashes (SIGABRT, SIGSEGV) during normal traffic — these are a strong indicator of exploitation attempts. Unusual multipart POST requests containing `PHP_SESSION_UPLOAD_PROGRESS` fields to endpoints that do not expect file uploads are suspicious.
| `docker/docker-compose.yml` | Vulnerable target container |
| `docker/php.ini` | PHP config (display_errors, memory_limit, session) |
| `docker/apache.conf` | Single-worker prefork config |
| `docker/offsets.py` | Auto-read libphp offsets from container ELF |
| `docker/www/index.php` | Minimal target page |
| `out/` | Proof output and offset cache (volume-mounted) |
- [PHP security advisory 2026-10-21](
- [GHSA-w8hw-w9fx-j79w]( — CVE-2026-109618
- [GHSA-6wmf-4rv5-p3rp]( — CVE-2026-109619
- [Patch commit 3584a98](
- [Sansec analysis](
- [The Hacker News coverage](
- [BleepingComputer coverage](
- [Brandefense advisory](
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.
