CWE-502 / Critical

Insecure Deserialization (CWE-502): From Serialized Objects to Remote Code Execution

How evaluating untrusted byte streams and objects in Python (pickle), Java (readObject), and PHP (unserialize) allows attackers to trigger arbitrary execution gadget chains.

💡 Plain English Explainer (ELI5)

Serialization is like disassembling a Lego castle into numbered instructions to mail it in an envelope, and deserialization is rebuilding the castle from the instructions. Insecure deserialization is when the receiver blindly follows any instruction in the envelope—including instructions that tell the builder to set fire to the living room.

Core Concepts & Key Terms

Serialization / Deserialization
Converting in-memory objects into byte streams for storage or network transport, and reconstructing objects back into runtime memory.
Magic Methods
Language-specific hooks (like Python's `__reduce__`, Java's `readObject`, or PHP's `__wakeup`) executed automatically during object instantiation.
Gadget Chain
A sequence of legitimate code snippets present in libraries on the classpath that, when chained together, achieve arbitrary code execution.
Type Confusion
Coercing an object mapper into creating an unexpected class type during deserialization.

Step-by-Step Attack Flow

Step 1

1. Malicious Payload Serialization

An attacker crafts a serialized object containing a magic method (e.g. __reduce__ in Python) configured to call os.system('id').

Step 2

2. Transmitting Serialized Stream

The attacker base64-encodes the byte stream and submits it via a cookie, session token, or API request payload.

Step 3

3. Unchecked Unpickling / Deserialization

The server passes the raw bytes directly to pickle.loads() or Java's ObjectInputStream.readObject().

Step 4

4. Instant Arbitrary Code Execution

Before the application logic even inspects the object, the runtime engine executes the constructor hooks, granting the attacker a shell.

Source Code: Flaw vs. Secure Implementation

VULNERABLE PATTERN
# VULNERABLE: Unpickling Untrusted User Data
import pickle
import base64
from flask import Flask, request

app = Flask(__name__)

@app.route("/api/load_session", methods=["POST"])
def load_session():
    cookie = request.headers.get("X-Session-Data")
    # FATAL FLAW: pickle.loads executes arbitrary Python bytecode!
    # An attacker providing a payload with __reduce__ gets immediate RCE!
    raw_bytes = base64.b64decode(cookie)
    user_session = pickle.loads(raw_bytes) 
    return f"Welcome back, {user_session.username}"
HARDENED DEFENSE
# SECURE: Safe Deterministic Data Formats (JSON + Cryptographic Signing)
import json
import hmac
import hashlib
import base64
from flask import Flask, request, abort

app = Flask(__name__)
SECRET_KEY = b"secure-production-secret-key-32b"

def verify_and_load(signed_payload: str) -> dict:
    try:
        data_b64, signature = signed_payload.split(".", 1)
        data_bytes = base64.urlsafe_b64decode(data_b64)
        expected_sig = hmac.new(SECRET_KEY, data_bytes, hashlib.sha256).hexdigest()
        
        # Constant-time signature verification prevents tampering
        if not hmac.compare_digest(expected_sig, signature):
            abort(401, "Invalid session signature")
            
        # Parse purely as primitive data types, NEVER executable code
        return json.loads(data_bytes.decode("utf-8"))
    except Exception:
        abort(400, "Malformed session token")

Engineering Hardening Checklist

← Browse Full Security Directory Explore Vulnerability Playbooks →