Skip to content
Back to Blog
·9 min read·technology

Insecure Deserialization: Exploitation and Defence Explained

How insecure deserialization lets attackers turn untrusted data into remote code execution, and how Indian dev teams can detect and defend against it.

BR

Bachao.AI Research Team

Cybersecurity Research

Scan Your Attack Surface

Security exposure this creates

Unpatched vulnerabilities in your tech stack are the #1 entry point for breaches targeting Indian businesses. Here's what to watch.

Insecure deserialization is what happens when an application rebuilds objects from serialized data — a byte stream, a base64 blob, a pickled Python object — without verifying that the data actually came from a trusted source or was never tampered with. Because deserialization in Java, .NET, PHP, and Python can trigger code execution as a side effect of simply reconstructing an object, an attacker who controls the serialized input can often turn "just loading some data" into full remote code execution (RCE) on the server. It sits inside OWASP's A08:2021 Software and Data Integrity Failures category, and for Indian dev and security teams shipping APIs, session tokens, and message queues fast, it is one of the most under-tested bug classes in production.

This guide covers how deserialization attacks actually work — object injection, gadget chains, and magic methods — what real-world impact looks like, how teams discover it before attackers do, and the concrete defences that close it off for good.

What Insecure Deserialization Actually Is

Serialization converts an in-memory object into a storable or transmittable format — a byte stream in Java, a string in PHP's serialize(), a byte stream via Python's pickle, or a binary blob via .NET's BinaryFormatter. Deserialization reverses that process, reading the format back into a live object in memory. The vulnerability isn't in serialization itself — it's in deserializing data the application does not control, because reconstructing an object is never just "reading data." It's executing code.

Where Untrusted Data Sneaks In

Untrusted serialized data typically enters through places developers don't expect to be attack surface:

    1. Session tokens or "remember me" cookies stored as serialized objects instead of opaque IDs
    2. API parameters, hidden form fields, or ViewState blobs that carry serialized state between requests
    3. Message queues and caching layers (Redis, RabbitMQ) where producers and consumers trust each other implicitly
    4. File uploads or import features that accept a serialized payload as a shortcut for "bulk data"
Any of these becomes a delivery mechanism the moment an attacker can modify the bytes before they reach the deserializer.

How Object Injection Becomes Remote Code Execution

The exploit path runs through what security researchers call a gadget chain. A "gadget" is any class already present in the application or its dependencies whose constructor, destructor, or a special "magic method" performs some action automatically the moment an object of that type is created or destroyed — no attacker code needs to be uploaded at all, because the vulnerable classes are already sitting in memory.

Each language exposes its own magic methods as the entry point:

    1. JavareadObject() and readResolve() are invoked automatically by ObjectInputStream during deserialization.
    2. PHPwakeup() and destruct() fire automatically when an object is unserialized or garbage-collected.
    3. Pythonpickle.loads() calls reduce() and setstate(), and can execute arbitrary functions specified inside the pickled payload.
    4. .NETBinaryFormatter and similar formatters trigger OnDeserialization callbacks and constructor logic during type reconstruction.
An attacker doesn't need to find one dangerous method — they need to chain several ordinary, legitimate ones. A gadget chain strings together classes whose side effects, combined in sequence, ultimately reach a "sink" — a method like Runtime.exec(), eval(), or os.system() that runs an attacker-supplied command. Public tools such as ysoserial (Java) and ysoserial.net (.NET) exist specifically to auto-generate these chains from common libraries already present on the classpath, which is exactly why the attack is so reliable against unpatched, widely-used frameworks.
graph TD A[Untrusted data received] --> B[Application deserializes it] B --> C[Magic method fires] C --> D[Gadget chain triggers] D --> E[Code execution on server] E --> F[Remediate and patch] style A fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0 style B fill:#1e3a5f,stroke:#3B82F6,color:#e2e8f0 style C fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0 style D fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0 style E fill:#5f1e1e,stroke:#EF4444,color:#e2e8f0 style F fill:#1e3d2f,stroke:#10B981,color:#e2e8f0
🚨
DANGER
Insecure deserialization frequently skips every access control and input validation layer an application has, because the exploit fires inside the deserialization call itself — before the application's own business logic ever runs. A WAF rule looking for SQL syntax or script tags will not catch a base64-encoded Java object graph.

Real Impact: Why This Bug Class Is Rated So High

The clearest large-scale demonstration was CVE-2017-9805 in the Apache Struts 2 REST plugin, where the framework used XStream to deserialize XML requests without any type filtering. Any unauthenticated request to a vulnerable endpoint could trigger full remote code execution, and the flaw was actively exploited in the wild within weeks of disclosure, landing it in CISA's Known Exploited Vulnerabilities catalogue. It is a textbook case of the pattern: a mainstream framework, a widely deployed plugin, and one missing allow-list turning into a mass-exploitable RCE.

🛡️
SECURITY
Deserialization bugs are rated so severely precisely because the "exploitability" step and the "impact" step are the same step. There's no separate privilege-escalation stage required — successful exploitation of the deserialization call is, by itself, arbitrary code execution.
7.94OWASP-weighted average impact score for the A08:2021 category covering insecure deserialization (OWASP Top 10 2021)
1,152CVEs mapped to the Software and Data Integrity Failures category, which absorbed insecure deserialization in the 2021 revision (OWASP Top 10 2021)

Know your vulnerabilities before attackers do

Run a free VAPT scan — takes 5 minutes, no signup required.

Book Your Free Scan

Language by Language: Where the Danger Lives

Every major stack has a native, "convenient" deserialization mechanism that was never designed with untrusted input in mind. Knowing which call sites to search for is the fastest way to scope an assessment.

Language / PlatformDangerous Native CallTypical Attack Entry Point
JavaObjectInputStream.readObject()Session cookies, RMI, JMS message bodies
PHPunserialize()Cookies, cached objects, form fields
Pythonpickle.loads(), yaml.load() (unsafe mode)Cache values, inter-service messages, ML model files
.NETBinaryFormatter, SoapFormatter, NetDataContractSerializerViewState, cached session objects, WCF endpoints
RubyMarshal.load()Session cookies, cached data
The common thread across every row: the call reconstructs a fully-typed, live object graph directly from attacker-influenced bytes, and the language runtime executes whatever hooks fire along the way — completely independent of the framework's own authentication or authorization logic.
pie title Deserialization Risk Share by Vulnerability Class "Gadget chain RCE" : 45 "Object injection tampering" : 25 "Denial of service payloads" : 15 "Type confusion" : 15

How Teams Discover Insecure Deserialization

Deserialization flaws rarely show up in a basic automated scan, because a scanner needs to recognise a serialized format, know the target language's gadget landscape, and actually trigger a callback to confirm the finding — not just flag a suspicious-looking string. A meaningful assessment combines:

  1. Code review for native deserialization calls — grepping for readObject, unserialize, pickle.loads, BinaryFormatter, and equivalents, then tracing whether the input reaching them is attacker-controlled.
  2. Dependency inventory — gadget chains rely on classes already present on the classpath or in installed packages. Knowing exactly which libraries (and versions) are loaded tells you whether a known public gadget chain applies.
  3. Dynamic testing with proof-of-concept payloads — tools like ysoserial generate serialized payloads for known gadget chains in common Java libraries, used in a controlled test to confirm exploitability rather than just theorise it.
  4. Fuzzing serialized inputs — mutating a legitimate serialized blob and observing crashes, errors, or unexpected object state changes, which often surfaces object-injection logic flaws even where full RCE isn't achievable.
The OWASP Deserialization Cheat Sheet is the reference checklist most assessment teams work from when scoping this testing across Java, PHP, Python, and .NET codebases.
💡
TIP
If your application accepts any base64-looking blob in a cookie, hidden field, or header that you didn't consciously design, decode it. rO0AB at the start is the signature of a serialized Java object; O:8:"ClassName" is PHP's unserialize() format. Either one showing up where you expected an opaque token is worth immediate investigation.

Defence: How to Actually Stop It

The most reliable fix is architectural, not patch-level: stop deserializing untrusted input into native objects at all.

    1. Prefer data-only formats. JSON and well-validated XML (with external entities disabled) carry data, not executable object graphs — deserializing them into simple data-transfer objects, with strict schema validation, removes the magic-method attack surface entirely.
    2. Never deserialize untrusted input with a native, unrestricted deserializer. If a legacy system genuinely requires native serialization internally, keep it confined to trusted, internal-only channels — never expose it to anything reachable from outside your trust boundary.
    3. Enforce class/type allow-lists. Java's ObjectInputFilter (since JEP 290), and equivalent allow-list mechanisms in other languages, restrict deserialization to a fixed, reviewed set of expected classes, so an attacker-supplied gadget class is rejected before its constructor ever runs.
    4. Add integrity checks. Sign serialized data with an HMAC using a server-side secret, and verify the signature before deserializing anything. This doesn't stop a determined attacker who has already found another way in, but it blocks tampering with data that merely passed through the client.
    5. Run deserialization with least privilege. If native deserialization can't be avoided, isolate it in a low-privilege process or sandboxed environment so a successful gadget chain can't reach the rest of the system.
    6. Keep frameworks and libraries patched. Struts, Jackson, XStream, and similar libraries have shipped fixes for known gadget classes repeatedly — an outdated dependency is often the entire vulnerability.
⚠️
WARNING
Blacklisting known-dangerous classes is a losing game — new gadget chains are discovered continuously in libraries you didn't even know were on your classpath. Allow-listing what's expected, not blocking what's known-bad, is the only approach that holds up over time.
🎯Key Takeaway
Insecure deserialization turns "loading data" into "running code" because magic methods and constructors fire automatically during object reconstruction — before any application-level authentication check runs. The durable fix isn't a smarter filter on the deserializer; it's removing native deserialization of untrusted input entirely in favour of validated, data-only formats like JSON, backed by allow-lists and integrity checks wherever legacy binary formats can't yet be retired.

Building This Into Your Security Programme

Insecure deserialization rarely shows up in a generic vulnerability scan the way an outdated TLS cipher or a missing security header does — it needs someone to actually trace which endpoints accept serialized input and whether the libraries behind them carry known gadget chains. That's exactly the kind of finding a structured, code-aware security assessment is built to catch, rather than a surface-level automated crawl.

At Bachao.AI, built by Dhisattva AI Pvt Ltd, automated VAPT assessments are designed to surface exactly these deeper, logic-level findings alongside the standard OWASP Top 10 coverage — including deserialization entry points, outdated serialization libraries, and missing integrity checks — instead of stopping at what a generic scanner flags. If your team hasn't specifically checked for native deserialization of untrusted input, a free VAPT scan is a fast way to find out where it's exposed. Teams handling regulated personal data can also review our DPDP compliance guide for how a code-execution vulnerability like this maps to breach-notification obligations, and our blog has more deep-dive guides like this one.

Frequently Asked Questions

What is insecure deserialization in simple terms?
It's what happens when an application rebuilds an object directly from data it received — a cookie, a request parameter, a cached blob — without verifying the data is trustworthy. Because reconstructing an object can automatically trigger code (via constructors or "magic methods"), an attacker who controls that data can often make the server execute arbitrary commands.
What is a gadget chain?
A gadget chain is a sequence of ordinary classes, already present in the application or its libraries, whose side effects — when triggered one after another during deserialization — end up calling a dangerous function like a shell command execution. No custom malicious code needs to be uploaded; the "gadgets" already exist in memory.
Which languages are affected by insecure deserialization?
Java (ObjectInputStream), PHP (unserialize()), Python (pickle), .NET (BinaryFormatter and related classes), and Ruby (Marshal) all expose native deserialization mechanisms that can trigger code execution. Any language whose deserializer can invoke constructors, destructors, or callback methods on attacker-influenced data carries the same risk pattern.
How do you defend against insecure deserialization?
Avoid deserializing untrusted input with native, unrestricted deserializers altogether — prefer JSON or similarly validated, data-only formats with strict schemas. Where native formats can't be removed, enforce class allow-lists, add HMAC-based integrity checks before deserializing, and run the deserialization step with least privilege so a successful exploit can't reach the rest of the system.
Is insecure deserialization still part of the OWASP Top 10?
It was its own category, A8:2017-Insecure Deserialization, in the 2017 OWASP Top 10. In the 2021 revision it was folded into the broader A08:2021-Software and Data Integrity Failures category, mapped to CWE-502 (Deserialization of Untrusted Data), reflecting that it's one facet of a wider integrity-verification problem across code, data, and CI/CD pipelines.
How do security teams find deserialization vulnerabilities before attackers do?
Through a combination of source-code review for native deserialization calls, dependency inventory to check for known-vulnerable serialization libraries, and controlled proof-of-concept testing with tools like ysoserial to confirm real exploitability rather than a theoretical finding.
BR

Bachao.AI Research Team

Cybersecurity Research

AI-powered security research and threat intelligence from the Bachao.AI team. Covering the latest vulnerabilities, CVEs, and cybersecurity developments affecting Indian businesses.

Get cybersecurity insights for Indian SMBs

Weekly vulnerability alerts, DPDP compliance tips, and security guides. No spam — unsubscribe anytime.

We respect your privacy. Your email is never shared.

Find out if you're exposed to this class of threat

Free automated scan — risk score in under 2 hours. No credit card required.

Scan Your Attack Surface
Find your vulnerabilitiesStart free scan →