Overview
Serialization is the process of converting an in-memory object into a byte stream or structured text so it can be stored or transmitted. Deserialization is the reverse: reconstructing an object from that representation. Insecure deserialization arises when an application deserializes data supplied or influenced by an attacker without verifying its integrity or constraining the permitted object types.
Because deserialization can trigger constructor calls, property setters, and magic methods during object reconstruction, an attacker who controls the serialized payload can often redirect execution through existing code ("gadget chains") to achieve outcomes far beyond what the application intended — most severely, arbitrary remote code execution (RCE) on the server.
How it works
The attack surface depends heavily on the runtime and serialization format, but the general mechanism is consistent:
- Attacker-controlled serialized data enters the application — via cookies, POST bodies, HTTP headers, API payloads, message queues, or shared caches.
- The application deserializes without validation — it reconstructs objects without checking a cryptographic signature, restricting allowed classes, or sanitizing field values.
- Gadget chains are triggered — the attacker crafts a payload whose object graph passes through classes already loaded in the JVM/CLR/interpreter that, in combination, execute attacker-supplied commands. Tools like ysoserial (Java), phpggc (PHP), and pwnlib (Python) automate gadget-chain discovery and payload generation.
- Impact materialises — depending on the gadget chain available, the outcome may be RCE, arbitrary file read/write, SSRF, denial of service, or authentication bypass.
Language-specific notes:
- Java:
ObjectInputStream.readObject()is the classic entry point. Dozens of gadget chains exist in common libraries (Apache Commons Collections, Spring Framework, etc.). - PHP:
unserialize()triggers__wakeup(),__destruct(), and__toString()magic methods on attacker-controlled classes. - Python:
pickle.loads()executes arbitrary Python via the__reduce__protocol; this is a design-level risk, not just an implementation flaw. - .NET:
BinaryFormatter,NetDataContractSerializer, andTypeNameHandlingin Newtonsoft.Json with type-name resolution enabled are well-known attack surfaces; Microsoft has deprecatedBinaryFormatterin .NET 5+. - YAML/XML: Parsers that resolve type tags (PyYAML
yaml.load(), XStream) can instantiate arbitrary classes.
Business impact
Successful exploitation can yield complete server compromise. Specific consequences include:
- Remote code execution: Attackers gain OS-level access, enabling data exfiltration, ransomware deployment, or use of the server as a pivot point.
- Authentication and authorisation bypass: Tampered session objects or role fields can grant administrative privileges without valid credentials.
- Data integrity violation: Attackers may modify in-flight business objects (e.g., prices, quantities, user IDs) before they are persisted.
- Denial of service: Deeply nested or circular object graphs can exhaust heap memory or CPU during reconstruction.
- Regulatory exposure: Server compromise typically triggers breach notification obligations under GDPR, HIPAA, PCI DSS, and similar frameworks, with associated fines and reputational damage.
How to fix it
- Avoid deserializing untrusted data entirely — prefer data-only formats (plain JSON without type hints, Protocol Buffers, MessagePack) that cannot instantiate arbitrary objects.
- Implement integrity verification — sign serialized payloads with an HMAC or digital signature and verify the signature before deserialization. Reject payloads whose signatures do not match.
- Apply strict allowlisting of deserializable types — in Java, wrap
ObjectInputStreamwith a customresolveClass()that rejects any class not on an explicit whitelist (e.g., using Apache Commons IO'sValidatingObjectInputStreamor the JEP 290 serial filter mechanism). In .NET, useTypeNameHandling.Nonein Newtonsoft.Json and avoidBinaryFormatter. - Upgrade or replace deprecated serializers — migrate away from
BinaryFormatter(.NET),picklefor untrusted data (Python), andyaml.load()without a safe loader (useyaml.safe_load()). - Run deserialization in a sandboxed context — confine the deserializing process to a low-privilege account, container, or OS-level sandbox (seccomp, AppArmor) to limit the blast radius of a successful exploit.
- Keep dependencies current — gadget chains often rely on vulnerable versions of third-party libraries. Regularly audit and update dependencies using SCA tooling.
- Log and monitor deserialization events — alert on unexpected class instantiation, unusually large payloads, or deserialization errors, which may indicate active probing.
- Conduct security testing — include deserialization attack payloads in penetration tests and use tools such as ysoserial or phpggc in controlled environments to verify exposure. Sensagraph automatically detects insecure deserialization indicators in web applications.
References
- OWASP Top 10 A08:2021 – Software and Data Integrity Failures
- OWASP Deserialization Cheat Sheet
- MITRE CWE-502: Deserialization of Untrusted Data
- JEP 290: Filter Incoming Serialization Data (OpenJDK)
- Microsoft: BinaryFormatter Security Guide
- Python Docs: pickle – Restricting Globals
- ysoserial – Java Deserialization Payload Generator (GitHub)
- PortSwigger Web Security Academy: Insecure Deserialization