Insecure Deserialization - Unsichere Deserialisierung
Insecure deserialization (OWASP A08:2021) occurs when an application processes serialized objects from untrusted sources without sufficient validation. Attackers manipulate serialized data to call arbitrary methods during deserialization (gadget chains). Affected are Java (readObject), PHP (unserialize), Python (native serialization formats), .NET (BinaryFormatter), and Ruby (Marshal.load). Result: Remote Code Execution, Privilege Escalation, or Denial of Service.
Insecure deserialization is one of the most complex classes of vulnerabilities—and at the same time, one with enormous potential for impact. The core problem: Serialization is a powerful mechanism that converts objects into a format that can be transmitted or stored. If deserialization processes this data without suspicion, the entire object structure becomes an attack vector.
Basic Principle
Serialization vs. Deserialization:
Serialization: Object → Bytes/Text (for transmission/storage)
Deserialization: Bytes/Text → Object (reconstruction)
Problem with insecure deserialization:
→ An attacker controls the serialized bytes/data
→ They can inject a specially crafted object
→ During deserialization, "magic" methods are called
→ Under normal program logic, these methods execute arbitrary code!
Where does serialized data occur?
→ HTTP cookies (Base64-encoded serialized session objects!)
→ HTTP body in POST requests
→ Database fields (BLOBs containing serialized objects)
→ Cache systems (Redis, Memcached)
→ Message queues (RabbitMQ, Kafka with Java objects)
→ API parameters and hidden form fields
Attack types by language
1. PHP deserialization:
PHP serialization format (recognizable):
O:4:"User":2:{s:4:"name";s:5:"Alice";s:4:"role";s:4:"user";}
→ Object (O) of the "User" class with 2 properties
Magic PHP methods called during deserialization:
__wakeup() → called automatically during deserialization
__destruct() → when the object is deleted after the script ends
__toString() → when the object is used as a string
Attack principle:
→ Identify a class with a dangerous __wakeup()/__destruct() method
→ Create your own serialized object of this class
→ Set a dangerous value → code execution during deserialization!
Typical use cases for PHP unserialize():
→ Cookie value, hidden form field, API parameter
→ Detection in code: grep -r "unserialize(" /var/www/
Protection:
→ Never use unserialize() on external data (without HMAC signature)
→ Use the allowed_classes parameter (PHP 7.0+):
unserialize($data, ["allowed_classes" => ["SafeClass"]])
→ Use JSON instead of PHP serialization for external data
2. Java deserialization:
Java Serialization: ObjectInputStream.readObject()
Identification characteristic in HTTP traffic:
→ "rO0AB" at the beginning (Base64) or "\xAC\xED\x00\x05" (bytes)
Gadget chains:
→ Gadget = class that performs actions useful to attackers during deserialization
→ In popular libraries: Commons Collections, Spring, Groovy, Hibernate
→ Chain: Gadget A calls B → B calls C → C executes code
Penetration testing tool ysoserial:
→ Generates crafted Java serialization objects for authorized testing
→ Supported chains: CommonsCollections1-7, Spring1, Groovy1
→ Payload → during deserialization: command execution on server
Java protection:
→ ObjectInputFilter (Java 9+): allowlist of permitted classes
→ Jackson/GSON/Protocol Buffers instead of Java native serialization
→ serialkiller filter: blocklist of known gadget classes
→ Deserialization in sandbox (isolated process)
3. Python native serialization formats:
Native Python serialization allows the __reduce__ method:
→ Class can call any function during deserialization
→ NEVER use for external/untrusted data!
Secure alternatives for Python:
→ json.dumps/json.loads: JSON without code execution risk
→ Pydantic, marshmallow: schema-based secure deserialization
→ Protocol Buffers, MessagePack: type-safe, no code execution
4. .NET BinaryFormatter (completely deprecated!):
BinaryFormatter: completely removed by Microsoft in .NET 7+
→ Was insecure by design: no class filtering
→ Migration: System.Text.Json, MessagePack, Protobuf
5. Ruby Marshal:
Marshal.load on external data: Ruby object gadget chains possible
Protection: Use JSON, MessagePack for all external data
Detection in Penetration Testing
Detecting deserialization in HTTP traffic:
Detection characteristics:
PHP serialized: O:4:"User":0:{} or a:2:{i:0;s:5:"hello";}
Java serialized: rO0ABVN... (Base64) or %AC%ED%00%05 (URL-encoded)
.NET: AAEAAAD/////...
Ruby Marshal: BAh... (Base64)
Burp Suite Procedure:
1. Analyze HTTP traffic with Burp: Cookies, body parameters, headers
2. Base64 value with "rO0AB" prefix → Java deserialization!
3. Install the Burp Extension "Java Deserialization Scanner"
4. Insert the prepared payload from ysoserial → into Burp Repeater
5. Out-of-Band verification: Burp Collaborator (DNS callback)
Automated scanning:
Nuclei: Templates for known deserialization vulnerabilities
Burp Pro Active Scan: automatically detects Java deserialization
Mitigation Measures
Secure Design Decisions:
Recommended Formats (NO code execution possible):
JSON: Simplest and safest option
MessagePack: Binary + compact + type-safe
Protocol Buffers: Schema-defined, Google standard
CBOR: Compact Binary Object Representation
Apache Avro: Schema-versioned, for big data
If deserialization is unavoidable:
1. HMAC signature over serialized data:
→ Only deserialize HMAC-signed data
→ Key on the server side (attacker cannot sign validly)
→ Verify signature before deserialization – failure → reject
2. Class allowlist:
Java: ObjectInputFilter → deserialize only allowed classes
PHP: allowed_classes parameter for unserialize()
.NET: System.Text.Json with known types
3. Sandbox isolation:
→ Deserialization in an isolated process (separate JVM/Python environment)
→ No network access, no filesystem access in the sandbox process
→ On exception: Kill process → no damage
4. Monitoring:
→ Log deserialization exceptions + SIEM alert
→ Unknown class names in payloads: Alert!
Developer checklist:
□ Never deserialize external data directly (without signature + allowlist)
□ Native serialization for external data? → Migrate to JSON/Protobuf immediately
□ Session data: Store on the server in the DB (no session object in the cookie)
□ If using cookie-based sessions: JWT with RS256 (stateless, no deserialization)
□ Keep libraries up to date: Gadget chains only in old versions!
□ SAST scan: Semgrep finds insecure deserialization calls in code