Skip to content
Exploit for Improperly Controlled Sequential Memory Allocation in Php

Exploit for Improperly Controlled Sequential Memory Allocation in Php

Sploitus • October 22, 2026

- **GHSA**: [GHSA-w8hw-w9fx-j79w]( — *"RCE via OOM corruption"*

- **Affected**: ` This tool may be used for legal purposes only. Users take full responsibility for any

> actions performed using this tool. Only run it against systems you own or are

> authorized to test. The author accepts no liability for damage caused by this tool.

A chunked multipart request has no usable declared `Content-Length`, and in vulnerable

builds `fill_buffer()` has no `post_max_size` cap — so attacker-controlled part-header

metadata is retained until `memory_limit` (128M stock) is exhausted. The failing

allocation makes `zend_mm_safe_error()` raise `E_ERROR` and then `zend_bailout()`

**longjmp** out of the multipart parser, unwinding a **half-initialized request**.

Request shutdown then proceeds from invalid state; the prefork worker **survives** and

keeps serving. A crafted heap layout converts that retained state into full remote code

execution (vendor-confirmed against mod_php with defaults).

Vendor-named OOM strategies: unusable declared `Content-Length`, **unlimited multipart

part headers**, empty file parts not counted by `max_file_uploads`, nested field names.

1. the OOM fatal during multipart parsing at **stock config** — the bailout trigger;

2. the pinned worker **survives** the bailout and answers the request on the

same connection — the half-initialized state path;

3. the **delta** vs fixed builds: `>= 8.2.35` stops reading at `post_max_size` — same

bytes, no OOM, no fatal — which identifies vulnerable targets.

Full RCE conversion requires heap grooming of the request-shutdown path and is

**not included** — no invented chain is claimed.

python3 cve-2026-109618.py -H TARGET -p 80 --path /index.php --mode headers --mb 160

python3 cve-2026-109618.py -H TARGET -p 80 --path /index.php --mode bigcd --mb 160

No external dependencies. Output against a vulnerable 8.2.34 target:

[2] flood sent=160MiB status=200 err=None (0.5s)

[+] OOM FATAL reproduced at stock config:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 4096 bytes)

[+] zend_mm_safe_error() -> E_ERROR -> zend_bailout() longjmp out of the parser

[+] worker SURVIVED the bailout and served the request

[+] PRIMITIVE CONFIRMED - OOM bailout corruption trigger reproduced.

`--mode bigcd` uses one huge `Content-Disposition` value — OOM on a 65 MB allocation

instead of 4 KB. The flood request carries `Authorization`/`Cookie` markers; if they

appear in the request on the same worker, the script flags stale state.

## Verification (reproduced on 8.2.34, delta on 8.2.35)

| Same request bytes | 8.2.34 stock | 8.2.35 stock |

| 160 MB part headers (chunked) | OOM fatal `allocate 4096`; worker survives | no OOM — partial upload (error 3), 2 MiB retained |

| 160 MB bigcd | OOM fatal `allocate 65015808`; worker survives | no OOM — error 3, 4 MiB retained |

| 160 MB file data (control) | no OOM; error 1, 2 MiB | no OOM; error 1, 2 MiB |

| 16 MB declared Content-Length | rejected pre-parse (`post_max_size`) | rejected pre-parse |

The fix removes reachability at stock config: `fill_buffer()` caps reads at

`post_max_size` (`main/rfc1867.c:249`), `php_mime_should_save_hdr()` allow-lists and

dedups part headers, and the allocator's delayed soft OOM (`EG(delayed_oom)` + VM

interrupt) replaces the `longjmp` bailout.

- [GHSA-w8hw-w9fx-j79w]( — php-src security advisory

- php.net: [8.2.35]( ·

[8.3.36]( ·

[8.4.27]( ·

[8.5.12](

- Fix commits: `01a40e89fbfc499936e3b05888d8f00bfde886df` (8.2) ·

`3c832b523ee13b1a010177d2ff84aea18be0015e` (8.3) ·

`506e87fa7dcdd306975e5b8f8265ee03527aacbf` (8.4)

- Regression test added by the fix: `Zend/tests/oom_request_parsing.phpt`

Extracted Entities

Attack Types (1)

Domains (1)

Platforms (1)

Vulnerabilities (1)