UAF exploit: exhaustive analysis, five independent RCE blockers documented
Type confusion via var_access confirmed: R:N; references resolve stale entries.data[0] without crashing, returning the attacker's own session data from the cached ZendMM chunk (pages retained, not zeroed). Key finding: filler spray and type confusion are mutually exclusive. var_tmp_var reads last_dtor (struct offset 16) BEFORE var_access reads entries (offset 56). Spray corrupts offset 16, crashing before the type confusion can execute. Built-in class audit: SplFileObject and PDO have function pointer calls in free_obj, but constructing fake objects requires libphp.so base (ASLR). Patch audit (8.5.12): all three defense layers are solid, no bypass found. Five RCE blockers: no info leak, filler/type-confusion mutual exclusion, GC header overlap, no usable classes without ASLR bypass, session key validation.
This commit is contained in:
+26
-15
@@ -101,27 +101,38 @@ The worker segfaults, confirming the use-after-free.
|
||||
| OOM during RFC1867 session decode | Confirmed |
|
||||
| Apache prefork worker survival after OOM | Confirmed |
|
||||
| Stale `BG(unserialize)` state across requests | Confirmed (level=1, data=freed ptr) |
|
||||
| Page reclamation via filler field spray | Confirmed (100 x 8200B = 300 pages) |
|
||||
| UAF crash (SIGSEGV in `var_push`) | Confirmed (first attempt, reproducible) |
|
||||
| Page reclamation via filler field spray | Confirmed (27+ fields crashes worker) |
|
||||
| UAF crash (SIGSEGV in `var_tmp_var`) | Confirmed (reproducible) |
|
||||
| Type confusion via `var_access` | Confirmed (stale zval, type matches OOM session) |
|
||||
|
||||
**Not achieved: full RCE.**
|
||||
|
||||
The struct's critical pointer fields (offsets 0-23) overlap with the
|
||||
`zend_string` GC header and cannot be controlled by the attacker:
|
||||
Two UAF primitives are confirmed, but they are mutually exclusive:
|
||||
|
||||
| Struct field | Offset | Overlaps with | Value |
|
||||
|---|---|---|---|
|
||||
| `last` | 0 | `gc_refcount` + `gc_type_info` | ~0x00000306_00000001 (invalid pointer) |
|
||||
| `first_dtor` | 8 | `hash` | 0 (lazily computed, safe) |
|
||||
| `last_dtor` | 16 | `len` | 8200 (low address, crash) |
|
||||
**1. Crash (filler spray path):** Filler fields reclaim the stale
|
||||
struct pages. `var_tmp_var` reads `last_dtor` at struct offset 16
|
||||
(now overwritten by the `zend_string` `len` field, e.g. 0x2008).
|
||||
Dereferences unmapped address. SIGSEGV.
|
||||
|
||||
Controlled bytes start at offset 24 (`allowed_classes`, `ref_props`,
|
||||
`max_depth`, `entries.used_slots`, `entries.next`, `entries.data[]`),
|
||||
but `var_push` crashes at offset 0 before reaching them.
|
||||
**2. Type confusion (no spray path):** Session data with `R:N;`
|
||||
backreferences resolves through `var_access`, which reads inline
|
||||
entries at struct offset 56. Without spray, entries.data[0] holds
|
||||
a stale pointer to the attacker's own data from request 1. The
|
||||
cached ZendMM chunk retains page data (only the header is zeroed).
|
||||
PHP reads the stale zval without crashing: type matches whatever
|
||||
the OOM session's first entry was (string, integer, etc.).
|
||||
|
||||
Full RCE would require either an info leak to construct a valid pointer
|
||||
in the GC header position, or a different allocation type that provides
|
||||
control over the first 24 bytes of an 8216-byte ZendMM allocation.
|
||||
The two primitives cannot coexist: `var_tmp_var` reads offset 16
|
||||
BEFORE `var_access` reads offset 56. Filler spray corrupts offset
|
||||
16, crashing before the type confusion can execute.
|
||||
|
||||
Full RCE is blocked by:
|
||||
1. **No info leak**: type confusion reads own data, not heap metadata
|
||||
2. **Mutual exclusion**: spray crashes `var_tmp_var` before `var_access`
|
||||
3. **GC header overlap**: uncontrolled bytes at offset 0-23 crash var_push
|
||||
4. **No usable classes**: SplFileObject/PDO have function pointer calls
|
||||
in `free_obj`, but fake objects need libphp.so base (ASLR)
|
||||
5. **Session key validation**: php serializer rejects keys containing `|`
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
Reference in New Issue
Block a user