Table of Contents
BBC Ch 14: Insecure Deserialization
Source: Bug Bounty Bootcamp by Vickie Li
Mechanisms
Serialization converts a program object into a format (byte stream or string) suitable for storage or network transfer. Deserialization reconstructs the object. Many languages support this: Java, PHP, Python, Ruby.
Developers often trust serialized data because it looks opaque or unreadable. Insecure deserialization occurs when user-supplied serialized data is deserialized without verification, letting attackers manipulate object properties or trigger dangerous magic methods.
PHP Object Injection
PHP serialize format example:
O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";s:9:"not admin";}
Format: O:CLASS_NAME_LENGTH:“CLASS_NAME”:PROPERTY_COUNT:{PROPERTIES}
Manipulating variables: if a serialized object is used for authentication and isn't signed or encrypted, modify the status field:
O:4:"User":2:{s:8:"username";s:6:"vickie";s:6:"status";s:5:"admin";}
Note: update the string length marker (s:9 → s:5) when changing values.
Magic methods: PHP auto-invokes certain methods during object lifecycle:
wakeup()– called when no references to the object remain– called when an object is deserialized (viaunserialize()) *destruct()toString()– called when an undefined method is invoked– called when the object is treated as a string *call()
If a magic method passes object properties into dangerous functions like eval(), an attacker can achieve RCE by controlling those properties in the serialized payload.
Classic PHP RCE via wakeup:
<code php>
class Example2 {
private $hook;
function wakeup() {
if (isset($this→hook)) eval($this→hook);
}
}
$user_data = unserialize($_COOKIE['data']);
</code>
Set $hook = “phpinfo();” in a crafted serialized object, URL-encode it, and pass it as the data cookie. The server calls eval(“phpinfo();”) on deserialization.
Generate the payload:
<code php>
class Example2 { private $hook = “phpinfo();”; }
print urlencode(serialize(new Example2));
</code>
===== POP Chains =====
When magic methods don't directly contain exploitable code, chain method calls across multiple classes (property-oriented programming). Each “gadget” is a code snippet from the existing codebase. Magic methods serve as the entry point; they call methods on other classes that eventually reach dangerous code like eval() or exec().
Example flow: __wakeup() calls $obj→evaluate() → CodeSnippet::evaluate() calls eval($this→code) → attacker controls $code.
===== Java Deserialization =====
Java serialized objects implement java.io.Serializable. Signatures that identify them:
* Hex: AC ED 00 05 at the start
* Base64: starts with rO0
* Content-Type header: application/x-java-serialized-object
Java deserialization exploits use gadget chains from libraries (Apache Commons-Collections, Spring Framework, Apache Groovy, Apache Commons FileUpload) to reach code execution.
Ysoserial automates gadget chain generation:
<code>
java -jar ysoserial.jar CommonsCollections1 'id'
</code>
Feed the output as a serialized object in the vulnerable parameter.
===== Prevention =====
* Never deserialize data tainted by user input without verification
* Use an allowlist to restrict which classes can be deserialized
* Prefer simple data types (strings, arrays) over serialized objects for transport
* Keep session state server-side instead of in serialized cookies
* Keep dependencies patched; many deserialization vulns come from third-party libraries
===== Hunting for Insecure Deserialization =====
With source code: search for dangerous deserialization functions:
* PHP: unserialize()
* Java: readObject()
* Python: pickle.loads()
* Ruby: Marshal.load()
Confirm that user-supplied data flows into these calls.
Without source code:
* Find large blobs of data in HTTP requests (cookies, POST body, headers) – these may be serialized objects
* Decode them: base64 blocks starting with Tzo or rO0 are likely PHP or Java objects
* Look for Content-Type: application/x-java-serialized-object
* Tamper with the object: change usernames, status flags, or role names; re-serialize and replay
* Try to achieve RCE using known gadget chains for the detected language
===== 6-Step Checklist =====
- Find entry points where user-supplied data is deserialized (cookies, POST params, headers).
- Identify the serialization format (PHP, Java, Python, Ruby) from signatures or Content-Type.
- Tamper with variable values in the serialized object (status, role, username) and check for privilege changes.
- If variable tampering achieves authentication bypass, document and report.
- See if magic methods or POP chains can escalate to RCE (use Ysoserial for Java).
- Draft report; be careful not to cause damage when testing RCE payloads.
