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

PoC and RCE derived from the PHP official patch [`3584a98bda`]( Includes a differential environment with both 8.2.34 and 8.2.35 versions of the container. The PoC and RCE are based on the pre-RINIT session decoding in PHP’s upload-progress handler.

- `exp/poc_pre_rinit.py` — Demonstrates that the upload-progress handler decodes the session before RINIT and executes object destructors, without executing commands.

- `exp/exp_rce.py` — Executes arbitrary commands at the same path, with results output via HTTP retrieval.

- `exp/poc_oom.py` — Triggers an OOM crash (CVE-2026-109618); only triggers, not actually causing a RCE.

- `lab/` — Versions `php:8.2.34-fpm` and `php:8.2.35-fpm`, each with nginx.

This chain was successfully tested on 8.2.34 but blocked on 8.2.35 due to the `incomplete_classes_only` rule. It does not involve zero-dependency exploitation: the target must have a class with dangerous magic methods before RINIT, and `lab` uses `opcache.preload` to simulate this.

Same code, same configuration, just different images:

| 8.1 | 8.1.34 (EOL, no official fix) | — |

The output of `/verify.sh` for the 8.2 branch:

Full output and call stack details can be found in [`docs/RESULTS.md`](docs/RESULTS.md).

The patch ensures that the upload-progress handler decodes the session before RINIT, and enforces `incomplete_classes_only` for this stage. Before the patch, this decoding would instantiate objects from the session record before script compilation. The process involves two steps, both of which go through HTTP:

1. `POST /app/upload.php`. The `user.ini` file sets `session.serialize_handler=php_serialize`; the upload-progress handler writes the multipart filename directly into the session file, along with the line `x|O:6:"PrePwn":...`.

2. `POST /api/pre.php`. The default `php` handler is used; there is no `session_start()` in the script. At the start of the request, `php_session_rfc1867_update()` still decodes: The `php` decoder cuts after the first `|`, passing the rest to `un serialize()`.

3. On 8.2.34, objects are created according to real classes; `__destruct()` calls `shell_exec($cmd)`, and the output is written to a random file in the webroot directory.

4. On 8.2.35, the same data is decompiled as `__PHP_Incomplete_Class`; there are no destructors or files. The payload is explicitly generated by `exp/lib2shell.py::php_object()`, with a length prefix calculated in bytes:

x|O:6:"PrePwn":2:{s:3:"cmd";s:12:"id; hostname";s:3:"out";s:33:"/var/www/html/.pwn_xxxxxxxx.txt";

Details the patch and code matches can be found in [`docs/DERIVATION.md`](docs/DERIVATION.md).

Docker Compose and Python 3.8+ (standard library only) are required.

./verify.sh # Create containers, run PoC/RCE, print differential matrix

docker compose -f lab/docker-compose.yml down -v

python3 exp/exp_rce.py --target --command 'id; uname -a'

python3 exp/poc_pre_rinit.py --target

python3 exp/poc_oom.py --target --size-mb 24

Ports: `18080` = 8.2.34 (vulnerable), `18081` = 8.2.35 (fixed).

Versions below 8.2.35, 8.3.36, 8.4.27, and 8.5.12 are vulnerable. Versions starting from 5.4.0 with `session.upload_progress` have the same pre-RINIT decoding; versions like 5.x, 7.x, 8.0, and 8.1 have been EOL, with no official fixes.

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

- `session.upload_progress.cleanup=Off` (default: On).

When `On`, entries for progress updates will be deleted after the request ends; this project does not override this setting, requiring additional mitigation measures.

- Both directories session storage, and the `serialize_handler` is different: `php_serialize` writes, while `php` reads.

This is a configuration error, not the default value. – RINIT already has available classes. The pre-RINIT path only recognizes `opcache.preload` or resident extension classes; the regular `session_start()` path (see below) can accept any automatically loaded gadgets. No authentication, user interaction, or application-layer vulnerabilities are required; both FPM and mod_php are valid. ## Impact Scope

It’s rare for all four conditions to occur simultaneously, so this vulnerability cannot be classified as “affecting 70% of websites.” It’s better to evaluate it as an “configuration error amplifier.” Once exploited, the impact includes arbitrary command execution under Web user privileges: reading `.env` and database passwords, writing webshells, connecting to the internal network, and lateral movement. In multi-tenant environments where session directories are shared, cross-tenancy issues may arise. Classes like `__wakeup`/`__unserialize` can also cause file writing and SSRF-like side effects. The same handler mismatch can also lead to other issues: if the `php` directory contains endpoints that call `session_start()`, triggering normal decoding via GET + Cookie. This also works on version 8.2.35 (tested). `incomplete_classes_only` only affects `PS(in_rfc1867_update)`; the patch that disables it is related to shortcuts before RINIT, not the configuration error itself. OOM triggers do not require any of these conditions: a single, approximately 24MB urlencoded request can cause a FPM worker to exit (`exit 70`), and this happens on both 8.2.34 and 8.2.35. This CVE has the most scalable impact, but it’s just a denial-of-service attack. ## Combined Exploitation

| Compatible Vulnerabilities | Conditions for Elimination/Evasion | Results |

| Arbitrary file writing, path traversal, backup recovery | Handler mismatch, uploadProgress, cleanup are not required | Simply writing session files into `save_path`; still results in RCE on 8.2.35 (tested) |

| SSRF attacks against Redis/Memcached session storage | Same as above, without disk storage | Using `SET` session keys is sufficient |

| Arbitrary file uploads with controllable filenames | Creating handler mismatches | Uploading `.user.ini` and setting `session.serialize_handler` (only for CGI/FPM) |

| Applications with `session_start()` endpoints | Version restrictions | Regular decoding paths; patches do not affect this (tested on 8.2.35) |

| LFI | Handler mismatch | Including session files in `include` executes PHP code (tested on 8.2.35) |

| Fixed sessions/SIDs | Requires the victim’s SID | Can write sessions → account takeover |

| Multi-tenant environments sharing `save_path` | None | Writing sessions to other vhosts, across tenants |

| OOM triggers | None | Disabling one worker per request directly causes DoS |

The most valuable one is the first one: writing `role|s:5:"admin"` into `save_path/sess_` can forge sessions; writing serialized objects into them and then accessing `session_start()` endpoints results in RCE. Both were successfully tested on 8.2.35. ## Project Structure

│ ├── lib2shell.py HTTP / multipart / serialize primitives

│ ├── poc_pre_rinit.py Destruction of destructors (no command execution)

│ ├── exp_rce.py Arbitrary command execution

│ └── poc_oom.py OOM trigger (only triggered)

│ ├── docker-compose.yml 8.2.34 + 8.2.35, each with nginx

│ ├── php.ini Shared configuration (cleanup=Off, opcache.preload)

│ ├── preload.php PrePwn / Probe / WakeProbe

│ ├── app/upload.php PHP serialization write points

│ └── api/ PHP read points (pre.php has no session_start, start.php has one)

│ ├── DERIVATION.md Patch hunk → primitives → code

│ └── RESULTS.md Test outputs and version matrix

The `exp_rce.py` command execution relies on the `PrePwn` gadget; the actual target should use framework gadgets visible before RINIT. This isn’t a limitation of the vulnerability itself, but rather a common prerequisite for deserialization exploitation. The project doesn’t implement zero-dependency OOM exploitation for CVE-2026-109618: that requires request heap layout, chunk reuse after release, and object storage for forgery. The patch only indicates which pointers are dirty, not the layout. `poc_oom.py` can only prove the trigger point; it cannot distinguish between “can be exploited” and “already fixed”. ## References

- Patch: `[php/php-src@3584a98bda]` (

- GHSA-w8hw-w9fx-j79w ( / CVE-2026-109618

- GHSA-6wmf-4rv5-p3rp ( / CVE-2026-109619

- Patches include tests: `ext/session/tests/rfc1867_no_user_code.phpt`, `Zend/tests/oom_request_parsing.phpt`

This is only for private environments or tests with written authorization. Users must assume full responsibility for compliance.