RepoPilotOpen in app →

frohoff/ysoserial

A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization.

Healthy

Healthy across all four use cases

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 5mo ago
  • 26+ active contributors
  • MIT licensed
Show all 7 evidence items →
  • CI configured
  • Tests present
  • Slowing — last commit 5mo ago
  • Concentrated ownership — top contributor handles 70% of recent commits

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/frohoff/ysoserial)](https://repopilot.app/r/frohoff/ysoserial)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/frohoff/ysoserial on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: frohoff/ysoserial

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/frohoff/ysoserial shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 5mo ago
  • 26+ active contributors
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Slowing — last commit 5mo ago
  • ⚠ Concentrated ownership — top contributor handles 70% of recent commits

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live frohoff/ysoserial repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/frohoff/ysoserial.

What it runs against: a local clone of frohoff/ysoserial — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in frohoff/ysoserial | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 185 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>frohoff/ysoserial</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of frohoff/ysoserial. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/frohoff/ysoserial.git
#   cd ysoserial
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of frohoff/ysoserial and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "frohoff/ysoserial(\\.git)?\\b" \\
  && ok "origin remote is frohoff/ysoserial" \\
  || miss "origin remote is not frohoff/ysoserial (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "src/main/java/ysoserial/GeneratePayload.java" \\
  && ok "src/main/java/ysoserial/GeneratePayload.java" \\
  || miss "missing critical file: src/main/java/ysoserial/GeneratePayload.java"
test -f "src/main/java/ysoserial/payloads/ObjectPayload.java" \\
  && ok "src/main/java/ysoserial/payloads/ObjectPayload.java" \\
  || miss "missing critical file: src/main/java/ysoserial/payloads/ObjectPayload.java"
test -f "src/main/java/ysoserial/payloads/util/Gadgets.java" \\
  && ok "src/main/java/ysoserial/payloads/util/Gadgets.java" \\
  || miss "missing critical file: src/main/java/ysoserial/payloads/util/Gadgets.java"
test -f "pom.xml" \\
  && ok "pom.xml" \\
  || miss "missing critical file: pom.xml"
test -f "src/main/java/ysoserial/Deserializer.java" \\
  && ok "src/main/java/ysoserial/Deserializer.java" \\
  || miss "missing critical file: src/main/java/ysoserial/Deserializer.java"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 185 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~155d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/frohoff/ysoserial"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

ysoserial is a proof-of-concept Java tool that generates serialized payloads exploiting unsafe Java object deserialization. It contains 50+ gadget chains (CommonsCollections1-7, Spring, Groovy, BeanShell, Hibernate, etc.) that automatically execute arbitrary commands when deserialized by vulnerable applications. The core capability is wrapping attacker-supplied commands in pre-built gadget chains, serializing them to stdout, and delivering them to applications that unsafely deserialize untrusted data. Monolithic single-jar structure: src/main/java/ysoserial/ contains three entry points (GeneratePayload.java, Serializer.java, Deserializer.java), src/main/java/ysoserial/payloads/ holds 50+ gadget chain implementations (one class per chain), and src/main/java/ysoserial/exploit/ holds target-specific exploitation adapters (JBoss, Jenkins, JRMPListener). Maven assembly plugin (assembly.xml) bundles everything into ysoserial-all.jar with GeneratePayload as mainClass.

👥Who it's for

Security researchers, penetration testers, and defensive security engineers who need to test Java applications for deserialization vulnerabilities. They use ysoserial to generate payloads for proof-of-concept exploits and to understand the attack surface of their own systems before patching.

🌱Maturity & risk

Actively maintained but not rapidly evolving. The project has sustained CI/CD via Travis and Appveyor, Docker support, and Maven-based builds (Java 1.6+ target for compatibility). Latest version is 0.0.6-SNAPSHOT. The codebase is stable—it's a well-established PoC tool from 2015 (AppSecCali talk) that serves its intended defensive/research purpose rather than a fast-moving feature product.

High inherent risk due to dual-use nature (explicitly designed as a weapon, with disclaimer in DISCLAIMER.txt). Dependency risks include reliance on old/vulnerable library versions (c3p0:0.9.5.2, commons-collections:3.2.2, bsh:2.0b5) that are known exploitable—this is intentional for testing but requires careful isolation. Single maintainer (@frohoff) represents maintenance risk, though issue activity and release cadence suggest adequate ongoing care.

Active areas of work

No specific active development visible in provided data. The project maintains CI/CD pipelines (.github/workflows/publish.yml, .travis.yml, appveyor.yml) but appears in maintenance/stable mode rather than active feature development. The version string (0.0.6-SNAPSHOT) and structure suggest a mature, well-tested tool rather than an evolving product.

🚀Get running

Clone and build with Maven:

git clone https://github.com/frohoff/ysoserial.git
cd ysoserial
mvn clean package -DskipTests
java -jar target/ysoserial-0.0.6-SNAPSHOT-all.jar

This generates the uber-jar and runs the usage message showing all available payload types and their dependencies.

Daily commands:

mvn package -DskipTests
java -jar target/ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections5 'touch /tmp/pwned' | xxd

The first argument is the payload type, the second is the shell command to wrap. Output is binary serialized Java object (suitable for piping to network sockets or files). Use -XDignore.symbol.file to suppress javac warnings about internal APIs.

🗺️Map of the codebase

  • src/main/java/ysoserial/GeneratePayload.java — Main entry point that orchestrates payload generation; every contributor must understand the CLI argument parsing and payload selection flow.
  • src/main/java/ysoserial/payloads/ObjectPayload.java — Core interface defining the payload abstraction; all gadget chain implementations extend this and contributors must understand the contract.
  • src/main/java/ysoserial/payloads/util/Gadgets.java — Utility class managing reflection-based gadget chain construction; critical for understanding how payloads leverage library classes without hard dependencies.
  • pom.xml — Maven configuration with all payload library dependencies; contributors need to understand version constraints and optional dependencies for each gadget chain.
  • src/main/java/ysoserial/Deserializer.java — Handles deserialization of payloads in testing/verification; essential for understanding the payload execution flow.
  • src/main/java/ysoserial/Serializer.java — Serializes gadget chains to binary format; paired with Deserializer to complete the exploit pipeline.
  • src/main/java/ysoserial/payloads/CommonsCollections1.java — Reference implementation of a gadget chain exploit; demonstrates the pattern all other payload classes follow.

🛠️How to make changes

Add a New Gadget Chain Payload

  1. Create a new class in src/main/java/ysoserial/payloads/ that implements ObjectPayload<Object> (src/main/java/ysoserial/payloads/MyNewPayload.java)
  2. Implement getObject(String cmd) method using Gadgets utility to dynamically chain library classes without hard dependencies (src/main/java/ysoserial/payloads/util/Gadgets.java)
  3. Add @Dependencies annotation listing required libraries (groupId:artifactId:version) (src/main/java/ysoserial/payloads/annotation/Dependencies.java)
  4. Add @Authors annotation crediting the discoverer (src/main/java/ysoserial/payloads/annotation/Authors.java)
  5. Register payload in GeneratePayload by adding entry to payload map (auto-discovered via reflection) (src/main/java/ysoserial/GeneratePayload.java)
  6. Add test case in src/test/java/ysoserial/test/payloads/PayloadsTest.java to verify gadget chain generation (src/test/java/ysoserial/test/payloads/PayloadsTest.java)

Add a New Exploit Delivery Mechanism

  1. Create new class in src/main/java/ysoserial/exploit/ implementing target protocol/service communication (src/main/java/ysoserial/exploit/MyNewExploit.java)
  2. Implement method to serialize and transmit payload using chosen ObjectPayload (src/main/java/ysoserial/Serializer.java)
  3. Add command-line invocation support in GeneratePayload main() method (src/main/java/ysoserial/GeneratePayload.java)
  4. Write integration test in src/test/java/ysoserial/test/exploit/ directory (src/test/java/ysoserial/test/exploit/RMIRegistryExploitTest.java)

Support a New Library with Gadget Chains

  1. Add new library dependency with appropriate version to pom.xml <dependencies> section (pom.xml)
  2. Research library classes that can be chained (typically factories, transformers, or callbacks) (src/main/java/ysoserial/payloads/util/Gadgets.java)
  3. Create 1-N payload classes in src/main/java/ysoserial/payloads/ using found gadget chains (src/main/java/ysoserial/payloads/MyLibraryChain1.java)
  4. Use Reflections and Gadgets utilities to avoid hard-coding library references (src/main/java/ysoserial/payloads/util/Reflections.java)
  5. Test all variants with CommandExecTest to ensure RCE works (src/test/java/ysoserial/test/payloads/CommandExecTest.java)

🪤Traps & gotchas

Java 1.6 source/target compatibility enforced in pom.xml with -XDignore.symbol.file flag—do not remove this or compiler will fail on internal API usage (e.g., sun.reflect). JDK version mismatch: compiling on Java 8+ while targeting 1.6 requires older javac or explicit source/target settings. Third-party gadget libraries must be exact versions specified in pom.xml (e.g., commons-collections:3.2.2, not 3.2.1 or 4.0)—version drift breaks gadget chains. Deserialization requires untrusted app to have the exact gadget libraries on its classpath; mismatches silently fail. RMI-based exploits require java.rmi.server.useCodebaseOnly=false on target JVM (Maven surefire plugin sets this in pom.xml for testing, but production apps rarely do). Binary serialization output is platform/JVM-specific; Java 8 vs 11 can produce different bytecode.

🏗️Architecture

💡Concepts to learn

  • Property-Oriented Programming (POP) gadget chains — ysoserial's entire design revolves around chaining getter/setter calls in legitimate library classes to achieve code execution; understanding POP is mandatory to grasp how each payload works and why library version mismatches break exploits.
  • Java object serialization (ObjectOutputStream/ObjectInputStream) — ysoserial generates binary serialized objects that are only harmful when deserialized; understanding Java's native serialization protocol (magic bytes, version IDs, field layouts) explains why payloads are JVM-specific and brittle.
  • Reflection-based method invocation in Java — Gadget chains like CommonsCollections1 use sun.reflect.ReflectionFactory and ChainedTransformer to indirectly invoke arbitrary methods during deserialization without explicit code calling them; this is the 'magic' that makes POP attacks work.
  • ClassPathXmlApplicationContext deserialization gadgets — Spring-based payloads (Spring1, Spring2) exploit Spring's config loading mechanism to load arbitrary beans from attacker-supplied URLs; understanding Spring's initialization flow is critical for these chains.
  • Java RMI (Remote Method Invocation) — ysoserial's JRMPListener and JRMPClient exploits use RMI registry and dynamic class loading (codebase parameter) to deliver gadget chains; RMI deserialization is a major attack surface for Java applications.
  • LazyMap and TransformedMap from Commons Collections — These two data structures are the foundation of nearly every CommonsCollections gadget chain in ysoserial; understanding their lazy-evaluation semantics is essential to grasping why deserialization triggers the chain.
  • InvokerTransformer and ChainedTransformer — These Commons Collections Transformer implementations use reflection to call arbitrary methods on arbitrary objects; they are the linchpin of CommonsCollections1 and the primary target of deserialization filter mitigations.
  • CommonsCollections/commons-collections — The gadget chain library source; understanding its ChainedTransformer and LazyMap implementations is essential to reverse-engineering how ysoserial exploits them.
  • spring-projects/spring-framework — Source of Spring gadget chains used by ysoserial's Spring1, Spring2 payloads; tracks patches that break gadget chains.
  • pwntester/marshalsec — Alternative Java deserialization gadget generator with overlapping payloads (CommonsCollections, Spring) but emphasizing .NET/CLR exploitation; directly comparable tool in the same space.
  • jacobsoo/marshalsec — Fork of marshalsec with additional gadget chains and active maintenance; users seeking alternatives or additional payload types often cross-reference.
  • swisskyrepo/PayloadsAllTheThings — Meta-repo documenting exploitation techniques (including ysoserial usage recipes) for Java deserialization; shows real-world attack integration patterns.

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for payload generation core classes

The repo has 30+ payload implementations but no visible test files in src/test/java. GeneratePayload.java, Serializer.java, and Deserializer.java are critical entry points that lack unit test coverage. This would catch regressions when adding new gadget chains and validate serialization/deserialization correctness.

  • [ ] Create src/test/java/ysoserial directory structure mirroring src/main/java
  • [ ] Add unit tests for Serializer.java covering successful and failed serialization cases
  • [ ] Add unit tests for Deserializer.java validating safe/unsafe deserialization behavior
  • [ ] Add integration tests for GeneratePayload.java with 3-5 representative payloads (CommonsCollections1, URLDNS, Spring1)
  • [ ] Configure maven-surefire-plugin in pom.xml to run tests during build cycle

Create GitHub Actions workflow for automated payload validation across Java versions

The repo targets Java 1.6+ (per pom.xml compiler settings) and has Travis/Appveyor CI, but no GitHub Actions workflow. A modern workflow would validate that payloads generate and serialize correctly across Java 8, 11, 17, and 21 to catch version-specific gadget chain breakage early.

  • [ ] Create .github/workflows/test-payloads.yml that runs on push/PR to main branches
  • [ ] Configure matrix build for Java versions: 8, 11, 17, 21 using setup-java@v3
  • [ ] Run mvn clean verify with surefire test reports
  • [ ] Add step to validate all payload classes can be instantiated via reflection (parse GeneratePayload.java's payload registry)
  • [ ] Upload test results as artifacts for payload generation logs per Java version

Add integration tests for exploit classes against mock RMI/JRMP targets

The exploit/ directory contains 9 classes (JRMPListener.java, JBoss.java, JenkinsCLI.java, etc.) that interact with external systems, but there are no tests validating their payload delivery logic. Mock-based tests would prevent regressions in network-dependent code without requiring real vulnerable servers.

  • [ ] Create src/test/java/ysoserial/exploit/ExploitTestBase.java as abstract test framework with mock server helpers
  • [ ] Add test for JRMPListener.java that mocks RMI registry and validates serialized payload transmission
  • [ ] Add test for JBoss.java validating HTTP request construction with generated payloads
  • [ ] Add test for JenkinsCLI.java mocking Jenkins CLI socket protocol
  • [ ] Use Mockito or EasyMock for HTTP/RMI mocking; document expected behavior in src/test/resources/EXPLOIT_TEST_README.md

🌿Good first issues

  • Add unit tests for newly added gadget chains under src/test/java/ysoserial/payloads/: CommonsCollections payloads lack parametrized tests covering Java 8, 11, 17 serialization format differences. Start by adding a @ParameterizedTest in a new PayloadTest.java.
  • Document each gadget chain with a brief description of the POP technique (e.g., 'CommonsCollections1: exploits LazyMap + ChainedTransformer reflection chain'). Create src/main/java/ysoserial/payloads/GADGET_CHAINS.md with one paragraph per payload explaining the vulnerability and prerequisites.
  • Expand exploit adapters in src/main/java/ysoserial/exploit/: add TomcatCLI.java and WildflyCLI.java following the pattern of JenkinsCLI.java. These app servers are commonly vulnerable but lack delivery helpers. Start with TomcatCLI wrapping a Commons Collections gadget into a Tomcat admin console RMI call.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 218bcff — Change base image to eclipse-temurin:8-jdk-alpine (#234) (darryk10)
  • b7d0f27 — Update Dockerfile (#193) (DoctorDrive)
  • 401bbbc — Update Dockerfile (#212) (aydinnyunus)
  • b03f958 — fixed dockerfile outdated java image to openjdk (#215) (yechielw)
  • bdbfef8 — fix travis link + add jitpack (#194) (noraj)
  • 863466d — fix assembly conf (#223) (frohoff)
  • ac3d16b — fix missing interceptors jar and repo order (#222) (frohoff)
  • 2874a69 — fix other download links (#191) (frohoff)
  • 91b4266 — update readme (#188) (frohoff)
  • 51110f9 — Publish uberjar as github release (#187) (frohoff)

🔒Security observations

Failed to generate security analysis.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · frohoff/ysoserial — RepoPilot