
Preuve de concept d'exploitation pour CVE-2026-33149, une injection d'en-tête Host dans Tandoor Recipes permettant l'empoisonnement de liens d'invitation et l'empoisonnement du cache. Inclut des modules pour l'acceptation d'hôte, la pagination, le schéma et l'empoisonnement des invitations.
Tandoor Recipes définit ALLOWED_HOSTS = '*' par défaut dans settings.py, ce qui amène Django à accepter n'importe quelle valeur dans l'en-tête HTTP Host sans validation. L'application utilise request.build_absolute_uri() pour générer des URL absolues dans plusieurs contextes sensibles à la sécurité. Un attaquant capable d'envoyer des requêtes avec un en-tête Host falsifié peut rediriger toutes les URL générées par le serveur vers un domaine contrôlé par l'attaquant.
L'impact le plus critique est l'empoisonnement des liens d'invitation : lorsqu'un administrateur crée une invitation utilisateur, l'e-mail envoyé par le serveur contient un lien pointant vers le domaine de l'attaquant. La victime clique sur un e-mail d'apparence légitime, le jeton d'invitation est exfiltré, et l'attaquant l'utilise pour détourner le processus de provisionnement du compte.
| Champ | Valeur |
|---|---|
| ID CVE | CVE-2026-33149 |
| GHSA | GHSA-x636-4jx6-xc4w |
| CWE | CWE-644 — Neutralisation incorrecte des en-têtes HTTP pour la syntaxe de script |
| CVSS v3.1 | 8.1 ÉLEVÉE — AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:N |
| Version affectée | Tandoor Recipes ≤ 2.5.3 |
| Fournisseur | TandoorRecipes/recipes |
| ID de technique | Nom | Pertinence |
|---|---|---|
| T1557 | Adversaire au milieu | Manipulation des URL générées par le serveur pour rediriger le trafic |
| T1566.002 | Hameçonnage : Lien d'hameçonnage ciblé | L'e-mail d'invitation empoisonné contient un lien contrôlé par l'attaquant |
Fichier : recipes/settings.py:118
ALLOWED_HOSTS = extract_comma_list('ALLOWED_HOSTS', '*') # default: wildcard
ALLOWED_HOSTS de Django est une mesure de sécurité qui valide l'en-tête Host par rapport à une liste blanche. Le caractère générique '*' désactive entièrement cette validation, permettant n'importe quelle valeur arbitraire.
Fichier : 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() construit les URL en utilisant request.get_host(), qui renvoie la valeur brute de l'en-tête Host lorsque ALLOWED_HOSTS ne la restreint pas. L'UUID d'invitation — un jeton secret — est intégré dans l'URL qui pointe désormais vers l'attaquant.
build_absolute_uri() pour les URL next/previous┌──────────┐ ① Requête falsifiée ┌─────────────────┐
│ Attaquant │ ──────────────────────────────→│ Serveur Tandoor │
│ │ Host: attacker.com │ ALLOWED_HOSTS=*│
└──────────┘ └────────┬────────┘
│
② build_absolute_uri()
utilise "attacker.com"
│
▼
┌────────────────┐
│ Serveur SMTP │
└────────┬───────┘
│
③ E-mail avec lien empoisonné :
http://attacker.com/invite/<uuid>
│
▼
┌────────────────┐
│ Victime │
│ (clique le lien) │
└────────┬───────┘
│
④ UUID envoyé à l'attaquant
│
▼
┌────────────────┐
│ Attaquant │
│ utilise l'UUID │
│ sur le vrai │
│ serveur │
└────────────────┘
requestspip install requests
# Exécuter tous les modules de validation avec authentification de base
python3 poc.py --target http://localhost:8085 \
--basic-auth admin:password \
--attacker evil.com \
--module all
# Exécuter tous les modules avec des cookies de session
python3 poc.py --target http://target:8085 \
--session <sessionid> \
--csrf <csrftoken> \
--attacker evil.com \
--module all
# Empoisonnement du lien d'invitation uniquement
python3 poc.py --target http://target:8085 \
--session <sessionid> \
--csrf <csrftoken> \
--attacker evil.com \
--module invite \
--email [email protected] \
--group-id 1
| Module | Description |
|---|---|
host-accept | Vérifie que la cible accepte des en-têtes Host arbitraires (ALLOWED_HOSTS = '*') |
pagination | Confirme que les URL de pagination API reflètent le domaine injecté |
schema | Confirme que les URL du serveur de schéma OpenAPI reflètent le domaine injecté |
invite | Crée un lien d'invitation empoisonné — le vecteur d'attaque principal |
all | Exécute tous les modules séquentiellement |
1. Acceptation de l'en-tête Host
curl -s -o /dev/null -w "%{http_code}" \
http://TARGET:8085/api/user/ \
-H "Host: attacker.com" \
-u "admin:password"
# Attendu : 200 (vulnérable) | 400 (corrigé)