Serialization converts in-memory objects into storable/transmittable formats (byte streams, JSON, XML). Deserialization reverses this. Vulnerabilities arise when untrusted data is deserialized, potentially triggering dangerous operations via magic methods (e.g., PHP's wakeup, Java's readObject). Attackers craft payloads to build gadget chains, leading to remote code execution, file read/write, or denial of service. Defense: never deserialize untrusted data; prefer JSON. If necessary, use integrity checks (signatures/encryption), class whitelists (e.g., Java's ObjectInputFilter), update dependencies, and isolate deserialization in sandboxes. PHP serialization uses a specific string format (e.g., O:6:"Person":3). Magic methods like wakeup and __destruct are attack entry points. Advanced exploits use POP chains. Tools like phpggc automate gadget chain generation. Bypasses include CVE-2016-7124 (wakeup bypass via property count manipulation) and phar deserialization.
1. What Are Serialization and Deserialization?
Serialization: Converting in-memory objects into a storable or transmittable format (byte streams, JSON, XML, etc.).
Deserialization: Restoring data in such formats back into program objects.
The vulnerability lies in the step of "deserializing untrusted data." While the program is reconstructing objects, certain dangerous operations may be triggered automatically — attackers exploit this to craft malicious data.
1.1 Introduction to Deserialization
It's essentially turning an object into transmittable characters, purely for the sake of easier transport.
Imagine this: we've written a class that holds some variables. After the class is instantiated, some variable values change during use. Later on, we may need those values again. If we keep the class alive indefinitely, waiting for the next time it's needed, it wastes system resources. In a small project this might not matter much, but as the project grows, small issues get magnified and cause real headaches. This is where PHP tells us: why not serialize the object, store it as a string, and bring it back out when you need it? That's what serializing objects for storage is all about.
In the context of PHP deserialization, we'll focus on two functions: serialize() and unserialize().
2. How Deserialization Vulnerabilities Arise
The serialize() and unserialize() functions themselves have no vulnerabilities in their PHP internal implementation. Deserialization vulnerabilities occur because of how applications handle objects, magic methods, and serialization-related issues.
When the parameter passed to unserialize() is controllable, a user can inject a carefully crafted payload. During deserialization, certain magic methods within the object may be triggered, leading to unintended consequences.
3. Typical Scenarios Across Different Languages
Many languages automatically invoke certain lifecycle methods on objects during deserialization.
Examples:
Java: readObject(), finalize();
PHP: __wakeup(), __destruct(), __toString()
Python: __reduce__(), __setstate__()
Attackers can't execute arbitrary code outright, but they can leverage existing classes within the application or third-party libraries, piecing together a call chain (Gadget Chain) like building blocks, ultimately achieving arbitrary command execution. Since the data is fully controlled by the attacker, even class names and field values can be tampered with.
Java: The most common case. Exploits ObjectInputStream to deserialize malicious Serialize() objects, combining with components like Commons Collections and Fastjson to complete the attack.
PHP: When unserialize() processes user input, magic methods are exploited to construct POP chains.
Python: The pickle module's loads() explicitly warns against deserializing untrusted data, because its virtual machine can be directly injected with system commands.
.NET: BinaryFormatter and SoapFormatter have been officially flagged as dangerous and should not be used with untrusted data.
Node.js: Third-party packages like node-serialize, if used to directly deserialize user-supplied data, can also lead to code injection.
4. Impact
Remote Code Execution: Gaining server access
File Read/Write: Stealing configurations, source code, or writing WebShells
Denial of Service: Constructing "deserialization bombs" through nested arrays, circular references, etc., exhausting CPU and memory
5. How to Defend
Never deserialize untrusted data (the most fundamental rule). Prefer pure data formats like JSON, and only use the library's pure data mapping mode.
When deserialization is unavoidable, perform integrity checks: apply digital signatures or encryption to serialized data to ensure it hasn't been tampered with.
Class whitelisting: strictly limit which classes are allowed to be deserialized (e.g., Java's ObjectInputFilter, PHP 7.3+'s allowed_classes option).
Update dependencies: keep base libraries and frameworks up to date, and avoid using older versions known to contain Gadget chains.
Isolation: perform deserialization operations inside a sandbox or a low-privilege process.
In summary: never trust serialized objects from the client side. If business requirements make it necessary, always add signatures and whitelist validation.
6. PHP Deserialization
6.1 Structure
To understand the attack, you first need to be able to read serialized strings. When you serialize an instance of a Person class, you'll get something like the following structure.
PHP
class Person {
public $name = "张三";
private $age = 20;
producted $sex = "boy"
}
TEXT
O:6:"Person":3:{s:4:"name";s:6:"张三";s:11:" Person age";i:20;s:6:" * sex";s:3:"boy";}
Object declaration: O:6:"Person":3 indicates an object (O), with the object name being 6 characters long, the class name being Person, and having 3 properties.
O: This is an object (Object)
6: The object name Person is 6 bytes long (P-e-r-s-o-n)
"Person": The object
3: This object has 3 properties
First property (public): s:4:"name";s:6:"张三";
s:4:"name": data type is string, length 4, content is "name".
s:6:"张三": data type is string, length is 6, content is "张三".
This is because "张三" occupies 6 bytes in UTF-8 encoding (each Chinese character takes 3 bytes). If you wrote s:2, it would cause a truncation error.
Second property (private): s:11:" Person age";i:20
This part is the trickiest and most important section of the entire string!
At first glance it looks like: " Person age" (one leading space, one space in the middle). But the length is 11 — let's count the hidden characters:
Actual storage: \x00+Person+\x00+age
Break it down: \x00 (1 byte) + Person (6 bytes) + \x00 (1 byte) + age (3 bytes)
When displayed as text, \x00 (null byte, invisible) typically appears as a space, hence why it's written as " Person age".
i:20: value is the integer 20
Third property (protected): s:6:" * sex";3:"boy";
At first glance it looks like " * sex", with a length that seems to be only 4,
but the actual length is 6:
Actual storage: \x00 (1 byte) + * (1 byte) + \x00 (1 byte) + sex (3 bytes)
The property name contains an asterisk, which means it's a protected property.
6.2 Dangerous Magic Methods
During the execution of unserialize(), if the class defines certain specific methods, PHP will automatically invoke them — these become the "entry points" for attacks:
__wakeup(): automatically called first during deserialization, often used for object resource initialization.
__destruct(): automatically called when the object is destroyed (e.g., at script termination), often used for "cleanup" work.
__toString(): triggered when the object is used as a string (e.g., echo $obj).
__call(): triggered when calling a method that does not exist or is inaccessible on the object.
__get()/__set(): triggered when reading/writing a property that does not exist or is inaccessible on the object.
6.3 Exploitation Techniques and POP Chain Construction
6.3.1 Basic Exploitation
Directly exploiting dangerous magic methods. For example, controlling the file deletion path inside __destruct() to achieve arbitrary file deletion.
6.3.2 Advanced Exploitation (POP Chains)
Using a magic method as the starting point, then calling methods of other classes, linking them together like a chain to ultimately execute high-risk operations.
When the value representing the number of properties in the serialized string is greater than the actual number, the execution of __wakeup() can be bypassed.
Phar deserialization: exploiting the phar:// pseudo-protocol to trigger metadata deserialization within file operation functions like file_exists(), representing an "unserialize()-less" exploitation technique.
1. What Are Serialization and Deserialization?
The vulnerability lies in the step of "deserializing untrusted data." While the program is reconstructing objects, certain dangerous operations may be triggered automatically — attackers exploit this to craft malicious data.
1.1 Introduction to Deserialization
2. How Deserialization Vulnerabilities Arise
The serialize() and unserialize() functions themselves have no vulnerabilities in their PHP internal implementation. Deserialization vulnerabilities occur because of how applications handle objects, magic methods, and serialization-related issues.
3. Typical Scenarios Across Different Languages
Many languages automatically invoke certain lifecycle methods on objects during deserialization.
Examples:
Attackers can't execute arbitrary code outright, but they can leverage existing classes within the application or third-party libraries, piecing together a call chain (Gadget Chain) like building blocks, ultimately achieving arbitrary command execution. Since the data is fully controlled by the attacker, even class names and field values can be tampered with.
4. Impact
5. How to Defend
In summary: never trust serialized objects from the client side. If business requirements make it necessary, always add signatures and whitelist validation.
6. PHP Deserialization
6.1 Structure
To understand the attack, you first need to be able to read serialized strings. When you serialize an instance of a Person class, you'll get something like the following structure.
6.2 Dangerous Magic Methods
During the execution of unserialize(), if the class defines certain specific methods, PHP will automatically invoke them — these become the "entry points" for attacks:
6.3 Exploitation Techniques and POP Chain Construction
6.3.1 Basic Exploitation
Directly exploiting dangerous magic methods. For example, controlling the file deletion path inside __destruct() to achieve arbitrary file deletion.
6.3.2 Advanced Exploitation (POP Chains)
Using a magic method as the starting point, then calling methods of other classes, linking them together like a chain to ultimately execute high-risk operations.
6.3.3 Automation Tools
https://github.com/ambionics/phpggc
6.3.4 Special Bypasses