Remote exploit for **CVE-2026-109618** (PHP `session.upload_progress` OOM corruption, CVSS 9.2). Pre-auth, application-independent: the entire chain runs inside PHP's request startup (rfc1867 multipart parsing + session upload-progress), before any application code executes.
| 0 | Abort prefork worker over HTTP → fresh heap state |
| 1 | OOM (`memory_limit` fatal) mid-multipart-parse leaves session globals dangling; request URL-encodes reclaimed heap into `Set-Cookie: PHPSESSID` → **ZendMM chunk base** |
| 2 | HashTable-size grooming; cookie bytes 8–16 hold a `zval_ptr_dtor` pointer → **libphp base** |
| 3 | Forge a fake `HashTable` inside a 167-byte `zend_string` (`pDestructor = zif_system`, fake `execute_data` with `num_args=1`, `arg1` = command) over `PS(http_session_vars)`; session teardown invokes `system()` |
python3 poc.py -u -c 'id>px.php' --fetch /px.php
Command is limited to 14 bytes (167-byte carrier minus header). For longer commands: `-c 'sh
libphp-relative, measured on the official `php:8.4.26-apache` image (`/usr/lib/apache2/modules/libphp.so`):
htdata = 0x690D8 (chunk-relative, 167-byte live string)
Other builds: extract with `readelf -sW libphp.so | grep -wE 'zif_system|zval_ptr_dtor'` and pass `--zif-offset/--dtor-offset`. `--htdata-offset` is heap-grooming dependent; sweep if stage 3 fails.
- mod_php only. php-fpm exits on startup fatals, so no surviving worker state (the fpm variant requires a post-startup application-level decode sink).
- Single-worker prefork. With multiple workers the stage-1 leak is unreliable (leak and use may land on different children); Wiz's public testing measured 0/10 with 8 workers.
- `display_errors = On` required — stage 1/2 need the fatal/heap content in the response; with it off the leak fails.
- Offsets are build-specific. Any libphp rebuild (distro patches, different PHP build) shifts `zif_system`/`zval_ptr_dtor`; the `htdata` offset additionally depends on heap layout and may need sweeping.
- Command ≤ 14 bytes (carrier size). Chain via `sh</path/file` for longer payloads.
- Expected side effect: the exploited worker aborts after execution (visible as SIGABRT/SIGSEGV child exits in Apache logs). The command still runs.
- ASLR is bypassed via the two leaks; no crash-based guessing, but heap grooming constants (`STAGE1_N`, `STAGE2_T/V`, `STAGE3_PADS`) were calibrated on the above image and may need re-tuning elsewhere.
- PHP advisory: GHSA-w8hw-w9fx-j79w (fixed 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12)
- Patched releases:
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.
