Skip to content

Serialized Object Detection

Samuele Giampieri edited this page Oct 7, 2026 · 7 revisions

📖 Canonical version: read this page on the official docs site — https://www.redamon.org/docs/serialized-object-detection. The GitHub wiki is a mirror.

Serialized Object Detection

Serialized Object Detection is RedAmon's insecure-deserialization capability. It is split into two halves that work as one loop:

  • Detect (recon). A passive, in-memory recon module — Serialized Object Scan — reads what the pipeline already holds (response headers, Set-Cookie, the parameters resource enumeration discovered, and the hidden and visible fields of the forms it crawled) and flags serialized-object signatures across every major family. Each sink becomes one info-severity :Vulnerability candidate per format, marked needs_agent_confirmation: a base64 Java cookie matches as rO0AB text and again once decoded, a serialization Content-Type is reported both by the probe and in its header list, and each is still one candidate. Recon sends no extra traffic and never deserializes anything.
  • Confirm (agent). The built-in Insecure Deserialization agent skill reads those candidates, confirms the one that matters with a non-destructive out-of-band oracle, and records proof so the candidate is promoted on the Priority Board.

The split is deliberate: recon flags "serialization is present and attacker-reachable" cheaply and safely; the agent does the request-side digging and the confirmation. A candidate stays a quiet lead until the agent proves it.


Why two halves

Serialized blobs mostly ride request-side — cookies, POST bodies, parameters. The recon pipeline's in-memory corpus only carries the response side plus the enumerated endpoints/parameters (request bodies are dropped). So recon seeds candidates from what it can see for free, and the agent — which has full captured-traffic access via its traffic tools — pulls the original request and confirms. This is why TrafficMind (HTTP capture) is a soft prerequisite: with it on, the agent has a request-side corpus to confirm against. The recon side still runs without it, just with a thinner corpus.


What it detects

The scan runs every candidate value through a bomb-safe decode-and-recurse normalizer (URL → base64 → gzip → zlib → hex, re-matching at each layer) and matches these families:

Family Signature deser_format
Native Java AC ED 00 05; base64 rO0AB; hex aced0005; Content-Type: application/x-java-serialized-object native_java
Jackson / json-io / Genson JSON @class key jackson_json
FastJSON JSON @type key fastjson
XMLDecoder <java version=, <object class=, <void xmldecoder
XStream FQ-class element / class= attribute xstream
SnakeYAML !! + a Java package tag snakeyaml
PHP O:<n>:", a:<n>:{; phar:// php_serialize / phar
Python pickle \x80\x02/\x04/\x05; base64 gASV / gAJ python_pickle
.NET BinaryFormatter / ViewState 00 01 00 00 00 FF FF FF FF; base64 AAEAAAD/////; __VIEWSTATE dotnet_binaryformatter / viewstate
Ruby Marshal \x04\x08 ruby_marshal
Hessian Content-Type: application/x-hessian hessian

Honest ceiling. Recon flags serialization present and reachable, not exploitable. It misses encrypted blobs, off-HTTP channels, and anything the in-memory slice never carried. The biggest blind spot is concrete: the slice holds response headers and Set-Cookie only for the URLs httpx probed: each base URL's root (after any redirect), plus the paths listed in httpxPaths, which is empty by default. The crawler keeps a deeper endpoint's path, parameters and forms, but not its response, so a blob that a page such as /login returns in a header or a cookie is missed unless that path is probed. Measured on the serialized-object lab with the defaults, a live run flags the families it also exposes in a link or a form, and none of the ten it serves only in deeper responses. Those are the agent's to find, from captured traffic. Confirmation is the agent's job.

Safety

  • Never deserializes. Standard-library decode + regex only — no pickle, no yaml.load, no ObjectInputStream-equivalent. The scanner never becomes the victim.
  • Bomb-safe decoder. At most 4 decode layers and ≤ 1 MiB of decompressed output per value; a decompression bomb is aborted and the candidate is flagged truncated rather than expanded.
  • Render-safe evidence. The stored evidence_snippet is printable-ASCII only (non-printable bytes hex-escaped), capped at 120 characters — never raw decoded bytes.

The candidate lifecycle

  1. Recon writes a :Vulnerability {source:'serialized_scan', needs_agent_confirmation:true, severity:'info'} candidate, linked to the affected Endpoint/BaseURL via HAS_VULNERABILITY, carrying deser_language, deser_format, deser_transport (cookie / param / header), deser_location (the cookie's, parameter's or header's own name), deser_encoding_layers (the layers peeled to reach the match, which a payload must be wrapped in again), deser_magic (the marker that matched) and evidence_snippet, plus Jev's deser_jev_* reading when the project ranks them with Jev. The candidate's id is its sink and format, so a later run that sees the same sink matched by another marker updates it rather than adding a twin.
  2. On the Priority Board the info severity pins it to T4 "Track" — the bottom of the list. It is a quiet lead, not noise at the top.
  3. The agent's Insecure Deserialization skill reads pending candidates (read-only query_graph), confirms with a non-destructive oracle (out-of-band callback, timing or error differential, as the project's switches allow), and records the confirmation as a chain_findings entry in its output analysis, with finding_type='vulnerability_confirmed' plus the candidate's id.
  4. That lands a proof-typed (:ChainFinding)-[:CONFIRMS]->(candidate) edge, which flows through the proof gate and promotes the candidate to T1 "Act now".

Until a candidate is confirmed, Pentest Reports and the Insights Dashboard hide it — an unconfirmed info candidate is never counted as a real vulnerability there. The graph screen, Node Inspector and Priority Board keep showing it, so an operator can always see the lead. Once confirmed, it appears in the report labelled "Insecure Deserialization".


The agent half: Insecure Deserialization skill

The built-in Insecure Deserialization agent skill (badge DESER, classification key deserialization) owns confirmation. See Agent Skills for the full write-up. In short, it:

  1. Reuses recon first — query_graph for pending serialized_scan candidates (most reachable first when Jev ranks them, then by id, with an explicit LIMIT), capturing each id and its deser_* metadata.
  2. Pulls the original request with its traffic tools (search by endpoint/method, or fetch_transaction) rather than re-crawling; if there are no candidates it probes from scratch using the same signatures.
  3. Confirms with a non-destructive oracle through the channels the project enables: an out-of-band callback (for example a Java URLDNS or a Python __reduce__-DNS blob that only performs a lookup), response timing, or the error differential. See the skill's switches.
  4. Records the confirmation by filling chain_findings in its output_analysis, in the same response that reads the proof, with finding_type='vulnerability_confirmed' and the candidate id. There is no report tool or action: the field is the only way a confirmation reaches the graph. It then re-queries to verify the CONFIRMS edge landed (a wrong/cross-tenant id matches nothing silently); if the edge is missing, the entry goes into the next response.
  5. Escalation to a code-execution gadget is gated off by default — the operator opts in per project. The default path is the non-destructive oracle only.

Model note. Recording depends on the model filling a structured field. In the end-to-end test against the lab, deepseek-chat confirmed the sink but kept writing "emit chain_findings" as a next step and never filled it, so no edge landed; deepseek-v4-pro filled it and the candidate was proven on the first try.


Settings

Both halves default off — a gated posture. An operator opts in per project.

Setting Where Default What it does
Serialized Object Scan (serializedScanEnabled) Project form → Recon Pipeline → JS Recon tab off Runs the passive detection module.
Rank candidates with Jev (serializedScanJevRank) Serialized Object Scan card, or Target & Modules → AI in Pipeline off Optional TypeSafe Jev assessment of each flagged blob (format + reachability). Writes Jev's reading onto each candidate and orders them for the agent. Needs AI in Pipeline and a Jev token on the owner's account.
Insecure Deserialization (deserialization built-in skill) Project form → AI Agent → Agent Skills off Lets the agent confirm candidates.
OOB callback workflow (deserializationOobCallbackEnabled) Agent Skills → Insecure Deserialization on The non-destructive interactsh oracle. Off = confirm via the timing and error channels only.
OOB provider (deserializationOobProvider) same card oast.fun interactsh server the oracle registers on.
Timing channel (deserializationTimingEnabled) same card on Confirm by response timing; disable on fragile targets.
Find sinks beyond recon candidates (deserializationFindSinksEnabled) same card on Sweep inputs for sinks recon never flagged. Off = confirm only recon's candidates.
Runtimes to cover (deserializationRuntimes) same card all seven Which per-runtime oracle blocks ship (java, java_typed, python, php, node, ruby, dotnet).
Exec gadget step (deserializationExecGadgetsEnabled) same card off Code-execution gadget delivery (Step 7). Needs the OOB callback on; off until an operator enables it.
PHAR polyglot upload (deserializationPharEnabled) same card off PHP PHAR polyglots, which upload a file to the target.

The full per-switch reference, with how each one changes the agent prompt, is on Agent Skills → Insecure Deserialization.

Because the module only flags candidates, the Serialized Object Scan settings card shows an alert when the scan is on but the Insecure Deserialization agent skill is off — pointing you to AI Agent → Attack Skills so the detect→confirm loop is not left half-wired. A second note recommends enabling TrafficMind so the agent has captured traffic to confirm against.

The Serialized Object Scan card in the JS Recon tab: the scan switched on, the Rank candidates with Jev control (Off | Jev), the alert that the Insecure Deserialization skill is off, and the TrafficMind note

Presets

Serialized Object Scan is pre-enabled in the web/API/active-focused presets: API Security Audit, Web App Pentester, Bug Bounty - Deep Dive, Parameter & Injection Surface, Full Pipeline - Active Only, and Full Pipeline - Maximum. Every other preset leaves it off.


Optional Jev ranking

With AI in Pipeline on, the scan can ask TypeSafe Jev two questions about each distinct blob it flagged: which serialization format it is (one of the scanner's 13 formats, or none), and how likely it is to be an attacker-reachable deserialization sink. Jev gets the blob's evidence only: its snippet, where it was seen and how it is encoded. It is never told the format the signatures matched, nor the marker label that names it, so its format answer is its own reading rather than the signatures' verdict read back.

Both answers are written onto the candidate: deser_jev_format (with deser_jev_format_confidence) and deser_jev_exploitability, 0 to 100. The Insecure Deserialization skill reads the pending candidates most reachable first, a none answer last, and when Jev's format disagrees with the signatures' it lets the evidence decide which oracle to build. A blob Jev could not answer keeps no deser_jev_* field and follows the answered ones, so without a token, out of credit or with Jev down, the scan is exactly the deterministic one, and a later run without Jev clears the fields. Each decision is also recorded in the recon output (jev_shadow.serialized_assess) and as jev-shadow serialized_assess drawer lines. It never drops a candidate and never rewrites the matched format, which is part of the candidate's graph id. It needs the scan itself on, and its control appears in the card once the scan is. Over MCP, preflight_scope_check reports it as the serialized_assess hook, off while the scan is off.


Pipeline position

Serialized Object Scan runs as a passive GROUP 5b step, beside JS Reconnaissance — after HTTP probing and resource enumeration (so the corpus it reads is populated), and it runs even when active scans are skipped. In the Recon Pipeline Workflow view it appears as the Serialized Objects node (passive badge) in the JS-Recon group, consuming BaseURL + Endpoint and producing Vulnerability candidates.

It is also available as a single-phase partial recon run from the workflow graph. A partial run works from what the graph keeps: every stored response header, Set-Cookie included, keyed by the URL that returned it; each endpoint's parameter names with up to five sample values; and the names of crawled form fields. Form field values are not in the graph, so a blob that rides only inside a hidden field's value is a full run's to find, and a URL you paste in the modal is scanned with no response data.


Reading candidates in the graph

Pending candidates awaiting confirmation:

MATCH (e:Endpoint)-[:HAS_VULNERABILITY]->(v:Vulnerability {source:'serialized_scan'})
WHERE v.needs_agent_confirmation = true
  AND NOT (:ChainFinding)-[:CONFIRMS]->(v)
RETURN e.url, v.id, v.deser_format, v.deser_transport, v.deser_location

The deser_* properties also appear in the Node Inspector and are filterable there. See Attack Surface Graph for the :Vulnerability schema.


Related

Clone this wiki locally