Back Sploitus Exploit for Improperly Controlled Sequential Memory Allocation in Php
**Pre-auth use-after-free in PHP core (request-body parser) → remote code execution.**
Root-cause analysis, affected-version detection, and a demonstration transcript.
> The vulnerability is in **PHP itself** (`main/rfc1867.c`, the session
> `upload_progress` path and the `BG(unserialize)` / `PS(id)` process-globals),
> not in any application. The HTTP-only ASLR bypass works against a bare PHP
> La vulnérabilité est dans **PHP lui-même**, pas dans une application. Le
> contournement d'ASLR (100 % HTTP) fonctionne contre une page PHP nue —
> **aucun CMS ni code applicatif requis**.
> This repository is **defensive and educational**: root-cause write-up and a
> passive **Nuclei** detection template. The weaponized exploit chain is **not
> published** (responsible disclosure). Update to a fixed PHP — see *Remediation*.
> Ce dépôt est **défensif et pédagogique** : analyse de cause racine et un
> template **Nuclei** de détection passive. La chaîne d'exploitation n'est **pas
> publiée** (divulgation responsable). Mettez PHP à jour — voir *Remédiation*.
| **CVSS 4.0** | 9.2 — `AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H` |
ASLR-bypass technique credit: 9bie (github.com/9bie/php2shell-bypass-aslr-exp)
00:00.08 OK DETECTED PHP/8.4.26 · Apache/2.4.69 (Debian)
00:00.14 OK HTTP vulnerable range (PHP forged-length PS(id) -> php_url_encode over-read
00:03.91 OK CAPTURE live heap url-encoded into Set-Cookie: PHPSESSID=
00:03.95 INFO CHUNK chunk base = 0x7f3d63200000 (2MiB-aligned mode)
00:03.98 INFO LIBPHP libphp base = 0x7f3d65264000 (pDestructor - ZVAL_PTR_DTOR)
00:49.84 INFO OVERRIDE fake reference destructor -> zif_system(command)
=====================================================================
00:58.91 OK RESULT PRE-AUTH RCE CONFIRMED
00:58.91 OK OUTPUT uid=33(www-data) gid=33(www-data) groups=33(www-data)
=====================================================================
Full transcript: [`transcript.txt`](transcript.txt).
When an allocation crosses `memory_limit`, the **unpatched** Zend memory manager
calls `zend_mm_safe_error()` (`Zend/zend_alloc.c`), which ends in
`zend_bailout()` — a **`longjmp()` out of the allocator itself**. The HTTP body
parser (`main/rfc1867.c`, `main/php_variables.c`), whose allocation sizes are
attacker-controlled (unbounded multipart part headers; chunked transfer bypassing
the `post_max_size` pre-check), is abandoned **mid-mutation of a request
The exploitable defect is an **ordering bug** in `php_request_startup()`
1873: php_hash_environment(); // POST parse -> upload-progress callback -> session decode (publishes BG(unserialize))
1874: zend_activate_modules(); // basic module RINIT -> memset(&BG(unserialize), 0, …), AFTER
The upload-progress session decode runs **before** the RINIT that clears
`BG(unserialize)`. If the OOM bails during that decode, the destroy is skipped
and the process-global `BG(unserialize)` is left **dangling** into a torn-down
request heap. The * * request on the same worker reuses it (its own RINIT
clear has not run yet — it is at line 1874, after the parse). A reclaiming spray
then reforges the dangling process-globals (`BG(unserialize)`, `PS(id)`,
`PS(http_session_vars)`), which is turned into a controlled destructor call →
**ASLR defeat — HTTP only, no CMS, no debugger.** The chain never needs local
access or an instantiated application object. A reclaimed `PS(id)` is reforged
with an attacker-chosen length; the session layer then `php_url_encode()`s it
into a `Set-Cookie: PHPSESSID=` header, **over-reading live heap** straight into
the HTTP response. The disclosed qwords give the 2 MiB-aligned heap chunk base
(by mode), and a leaked destructor pointer gives the libphp base
(`libphp = pDestructor − ZVAL_PTR_DTOR_OFFSET`); `system` is `zif_system` inside
libphp, so no libc leak is required. This remote Set-Cookie disclosure is the
technique of **9bie** ( it
works against a bare ` callback upload-progress -> décode session (publie BG(unserialize))
1874: zend_activate_modules(); // RINIT module « basic » -> memset(&BG(unserialize), 0, …), APRÈS
Le décode upload-progress tourne **avant** le RINIT qui remet `BG(unserialize)`
à zéro. Si l'OOM frappe pendant ce décode, le `destroy` est sauté et le global
process `BG(unserialize)` reste **dangling**. La requête suivante sur le même
worker le réutilise ; un spray de reclaim reforge les globals process dangling
(`BG(unserialize)`, `PS(id)`, `PS(http_session_vars)`), menés à un appel de
**Défaite de l'ASLR — 100 % HTTP, sans CMS ni débogueur.** La chaîne n'a besoin
ni d'accès local ni d'un objet applicatif instancié. Un `PS(id)` récupéré est
reforgé avec une longueur choisie par l'attaquant ; la couche session le
`php_url_encode()` ensuite dans un en-tête `Set-Cookie: PHPSESSID=`, **lisant
au-delà** du tampon directement dans la réponse HTTP. Les qwords divulgués
donnent la base du chunk de tas (aligné 2 Mio, par mode), et un pointeur de
destructeur divulgué donne la base de libphp
(`libphp = pDestructor − ZVAL_PTR_DTOR_OFFSET`) ; `system` est `zif_system` dans
libphp, donc aucune fuite de libc n'est requise. Cette divulgation Set-Cookie
( ; elle fonctionne contre une
page `<?php` nue avec `display_errors=On`.
**Le correctif** (8.4.27 / 8.5.12) ajoute `php_basic_early_rinit()`, **avant** le
parse (`main.c:1869`). Analyse détaillée : [`ROOTCAUSE.fr.md`](ROOTCAUSE.fr.md).
Passive, zero-impact version-range detection (Nuclei):
nuclei -t nuclei/cve-2026-109618.yaml -u
It parses the PHP banner and flags the affected ranges. A banner-suppressed host
(`expose_php=Off`) will not match — use a behavioural check in that case.
Template: [`nuclei/cve-2026-109618.yaml`](nuclei/cve-2026-109618.yaml).
- **Update to PHP ≥ 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12.** The fix closes the
- Temporary mitigation: disable `session.upload_progress.enabled`. Lowering
`memory_limit` does **not** help (the attacker sizes the OOM).
- Wiz — *PHP2Shell: PHP RCE* —
- Huntress — *PHP2Shell: Leaked Critical PHP Vulnerabilities*
- PHP security release 8.4.27 / 8.5.12 (fix commit `506e87fa7`)
- GHSA-w8hw-w9fx-j79w · GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)
- 9bie — *php2shell HTTP-only ASLR bypass (Set-Cookie heap disclosure)* —
Analysis and detection template released for defensive use. See [`LICENSE`](LICENSE).
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
