Insecure deserialization is what happens when an application converts attacker-controlled bytes back into objects without verifying they are safe. Because deserialization can invoke constructors, magic methods, and property setters, a crafted payload can chain existing classes (a gadget chain) into arbitrary behavior, frequently remote code execution. It affects Java (via libraries like Commons Collections), PHP (unserialize with magic methods), Python (pickle), .NET, and Ruby, and the root cause is trusting serialized input.
What insecure deserialization is
Serialization turns an object into a storable or transmittable form; deserialization rebuilds it. The danger is that rebuilding an object is not passive. Depending on the language, it can call constructors, magic methods such as PHP's __wakeup and __destruct or Java's readObject, and property setters.
If an attacker controls the serialized data, they control which of those code paths run and with what values. They rarely need to inject new code. Instead they assemble a gadget chain: a sequence of methods that already exist in the application's libraries which, when triggered in order during deserialization, produce a dangerous effect such as running a command.
The abuse and payload
Testers first spot serialized data, then build or reuse a gadget chain for the stack. Shown for defensive context:
- Spot it: Java serialized blobs start with the bytes
ac ed(base64rO0); PHP serialized strings look likeO:4:"User":...; Python pickle and .NET have their own signatures. - Java: a tool like ysoserial generates a payload from a known gadget chain, for example
java -jar ysoserial.jar CommonsCollections5 'id', delivered where the app deserializes it. - PHP: craft an object whose
__wakeupor__destructreaches a sensitive sink, a classic POP (property-oriented programming) chain. - Python: an object whose
__reduce__returns(os.system, ('id',))runs a command the momentpickle.loadsprocesses it.
Because exploitation happens during parsing, it is easy to miss in a code read and is a priority target in web app penetration testing.
How to defend
The reliable fix is to stop deserializing untrusted input into rich objects:
- Prefer a data-only format such as JSON with a strict schema, and never native serialization for data that crosses a trust boundary.
- If native serialization is unavoidable, add integrity protection, sign the payload and verify it before deserializing, and restrict allowed classes with a strict allowlist.
- Keep gadget-bearing libraries patched and minimal; every extra library on the classpath is another potential gadget source.
- Isolate and monitor deserialization endpoints so an attempt is logged and contained.
Treat any serialized blob arriving from a client, cookie, or message queue as hostile.
References
- [1]OWASP: Deserialization Cheat Sheet(OWASP)
- [2]PortSwigger: Insecure deserialization(PortSwigger)
- [3]MITRE ATT&CK: Exploit Public-Facing Application (T1190)(MITRE)