# Use After Free Vulnerability in unserialize()

PHP unserialize() with Serializable classes frees a zval on inner parse failure, then R:/r: references reuse it, yielding UAF and RCE.

- Payout: hidden
- Severity: critical
- Program: Internet Bug Bounty
- Found by: ryat
- Disclosed: 2019-10-13
- Write-up quality: 8/10
- Original: https://hackerone.com/reports/159948
- Library page: https://bugbountylibrary.com/r/h1%3A159948
- Tags: area:oss domain:oss impact:data-leak impact:rce kind:bounty platform:hackerone sev:critical tech:php technique:source-review vuln:deserialization vuln:memory-corruption

**Target:** PHP unserialize() / Serializable interface (object_custom)
**Root cause:** When php_var_unserialize() fails, zval_dtor() frees return_value and it is reset to FALSE, but a nested Serializable object still holds it; a later R:/r: reference points to the freed zval.
**Steps:**
1. Define class obj implements Serializable whose unserialize() calls unserialize($data).
2. Craft payload `a:2:{i:0;C:3:"obj":17:{C:3:"obj":3:{ryat}i:1;R:4;}` so the inner C: parse fails, freeing the zval.
3. Reference the freed slot with `R:4` to obtain a dangling zval.
4. For RCE, embed a fake zval via ptr2str() and reference it: `a:4:{i:0;i:1;i:1;C:3:"obj":...{...}i:2;s:N:"<fakezval>";i:3;R:5;}`.
5. Run `php uafpoc.php`; var_dump shows int(1122334455), confirming controlled fake zval.
**How found:** Reading object_custom() and PHP_FUNCTION(unserialize) error paths: noticed zval_dtor() frees return_value on failure while the Serializable object retains it, then reasoned R:/r: references could target the freed slot.
**Exploitation:** Attacker supplies a serialized string to unserialize(); nested Serializable failure frees a zval, R: reference reuses freed memory, and a crafted fake zval (array/object type) yields arbitrary code execution or memory leak.
**Escalation:** Fake zval of array/object type converts the UAF into remote code execution.
**Insight:** In deserializers, audit error/cleanup paths where a partially-built value is freed but still reachable via back-references (R:/r:); nested custom-unserialize callbacks that fail mid-parse are prime UAF triggers.

