Two critical vulnerabilities were recently disclosed in PHP affecting Apache servers running mod_php: CVE-2026-109618 and CVE-2026-109619. When chained together, they allow pre-authentication remote code execution — no credentials, no application-level bugs, no access to /proc or GDB. Just HTTP requests against the PHP runtime itself.
At DeepLook Labs, we spent time analyzing both vulnerabilities, understanding the exploitation chain, and building a functional exploit that achieves RCE under controlled conditions. In this post we share a brief overview of what these CVEs are about and what we were able to confirm.
The Vulnerabilities
CVE-2026-109618 — OOM Stale HashTable Bucket
This one lives in _zend_hash_str_add_or_update_i inside the Zend Engine. When an Out-of-Memory condition occurs during HTTP request processing, the HashTable can leave stale buckets — pointers to already-freed memory. On the next request handled by the same Apache prefork worker, those pointers get reused. Classic Use-After-Free.
CVE-2026-109619 — Dangling Session Globals
This one targets the session upload progress mechanism in ext/session. When OOM happens before PG(modules_activated)=1 — during multipart/form-data parsing with session upload progress enabled — the module shutdown function (zend_deactivate_modules()) is skipped entirely. The session RSHUTDOWN never executes, leaving PS(id) and PS(http_session_vars) pointing to per-request memory that has already been freed.
The next request that hits the same worker accesses those dangling pointers. Controllable UAF.
What We Confirmed
After analyzing the vulnerability chain described by the original researchers, we developed a functional exploit that achieves RCE through three stages:
-
Heap Leak — triggering the OOM to leave
PS(id)dangling, then spraying form fields to reclaim the memory and leak heap pointers viaSet-Cookie. The pointers aligned to 2 MB reveal the ZendMM chunk base. -
Code Leak — a second OOM with different padding to overlap HashTable buckets. The
pDestructorfield (a pointer tozval_ptr_dtorinside libphp) leaks out, giving us the libphp base address. Pure in-band ASLR bypass. -
Code Execution — reclaiming
PS(http_session_vars)with a fake HashTable whosepDestructorpoints tozif_system. When PHP cleans up the HashTable, it callspDestructor(arg1)→system("cmd").
The exploit was confirmed against PHP 8.4.26 on x86_64, achieving command execution as www-data. The lab setup used memory_limit=8M, session.upload_progress.enabled=On, and a single Apache worker (MaxRequestWorkers=1).
We also confirmed reliable crash/DoS (10/10 SIGABRT) on both PHP 8.2.34 (arm64) and PHP 8.4.26 (x86_64).
OOM Trigger Technique
The OOM is triggered through uploads with empty filenames and deeply nested field names:
Content-Disposition: form-data; name="f[0_0][0_1]...[0_14]"; filename=""
Each empty-filename upload consumes ~35 KB during multipart parsing, but the upload body itself can be empty — keeping the total POST body under 1 MB while exhausting the memory limit. With memory_limit=8M, roughly 200–400 uploads are enough.
Patched Versions
The fix landed in commit 3584a98bda and is available in:
- PHP 8.2.35
- PHP 8.3.36
- PHP 8.4.27
- PHP 8.5.12
Mitigation
If you can't update immediately:
- Disable session upload progress:
session.upload_progress.enabled=Off - Use PHP-FPM instead of mod_php (process isolation prevents worker reuse)
- Keep
max_file_uploadsat its default (20) - Never run
display_errors=Onin production
Credits
The original detailed writeup and the empty-filename amplification technique were documented by Calif / Wiz (Ryan Austin — @lcfr_hax), including the analysis of the BG(unserialize) flow and extension to WordPress environments.
Analysis, exploit development, and lab validation performed by Walleson Moura (@ph0r3nsic) at DeepLook Labs.
Peace!