
CVE-2026-8206: Kirki Customizer Framework - Account Takeover Non Autenticato (CVSS 9.8)
CVSS v3.1 9.8 / 10.0 CRITICO
Vettore: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVE-2026-8206 è una vulnerabilità di compromissione dell'account non autenticata nel framework Kirki Customizer, un popolare plugin per page builder di WordPress con oltre 90.000 installazioni attive. La vulnerabilità consente a un attaccante di assumere il controllo di qualsiasi account WordPress (incluso quello admin) semplicemente inviando una singola richiesta HTTP.
Il bug risiede nell'endpoint REST API kirki-forgot-password del modulo ComponentLibrary. Questo endpoint consente di richiedere un reset della password per qualsiasi utente e invia il link di reset a un indirizzo email specificato dall'attaccante, invece dell'email registrata dall'utente.
| Informazione | Dettaglio |
|---|---|
| Plugin | Kirki Customizer Framework (WordPress Page Builder) |
| Versioni Affette | 6.0.0 – 6.0.6 |
| Patch | 6.0.7 (o 6.0.12+) |
| Tipo di Vulnerabilità | Compromissione dell'Account non Autenticata tramite Dirottamento dell'Email di Reset Password |
| Endpoint | /index.php?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password (o /wp-json/KirkiComponentLibrary/v1/kirki-forgot-password) |
| CVSS | 9.8 CRITICO |
Prerequisiti per lo sfruttamento:
→ Ogni sito WordPress con Kirki 6.0.0–6.0.6 installato è vulnerabile per impostazione predefinita.
Kirki registra endpoint REST API per la funzionalità Component Library, inclusi login, registrazione, forgot-password e change-password. Tutti consentono l'accesso agli ospiti (guest_permissions_check restituisce true).
Il nonce di WordPress è un token di protezione CSRF. Kirki richiede un nonce tramite l'header X-WP-Element-Nonce oppure controlla un parametro nonce nella richiesta. Tuttavia, il nonce può essere generato/estratto pubblicamente dal lato client.
WordPress usa get_password_reset_key() per generare un token di reset della password. Questo token viene incluso da Kirki nel modello email prima dell'invio.
File: ComponentLibrary/controller/CompLibFormHandler.php
Nella versione vulnerabile (6.0.0–6.0.6), la funzione handle_forgot_password() non verifica se l'email fornita corrisponde all'email registrata dell'utente. Un attaccante può specificare il nome utente della vittima e la propria email: il link di reset verrà inviato all'email dell'attaccante.
Codice sorgente vulnerabile:
public function handle_forgot_password( $request ) {
$form_data = $request->get_body_params();
$this->validate_nonce( 'kirki-forgot-password' );
$email = $form_data['email']; // <-- ATTACKER CONTROLLED
$username = $form_data['username'];
$user = get_user_by( 'login', $username );
//BUG: Does NOT check $email === $user->user_email
// Attacker's email is used directly
$key = get_password_reset_key( $user );
$reset_link = "$url?action=rp&key=$key&login=$username";
// SENDS RESET LINK TO ATTACKER'S EMAIL
wp_mail( $email, $subject, $body_with_reset_link );
// ^^^^^
// ATTACKER'S EMAIL, NOT THE USER'S EMAIL
}
L'oggetto e il corpo dell'email vengono ricevuti dal client tramite i parametri emailSubject e emailBody senza alcuna firma HMAC per verificarne l'integrità. L'attaccante può modificare arbitrariamente il contenuto dell'email inviata.
// VULNERABLE VERSION: No HMAC verification
$email_subject = $form_data['emailSubject']; // Client-controlled
$email_body = $form_data['emailBody']; // Client-controlled
// No verify_email_template_signature()
// PATCHED VERSION (6.0.7+): HMAC verification / Email matching against DB
$user_email = $user->get( 'user_email' );
if ( $email !== $user_email ) {
return new WP_REST_Response( array( 'message' => 'If an account exists...' ), 200 );
}
| Fattore | Spiegazione |
|---|---|
| Nessuna autenticazione richiesta | guest_permissions_check() restituisce true — chiunque può chiamare l'endpoint |
| Qualsiasi account può essere compromesso | È necessario solo il nome utente (predefinito: 'admin') — non sono richieste email/password |
| Impatto: Compromissione admin | Compromissione admin → caricamento webshell → RCE → controllo completo del server |
| Diffusione | Kirki è un plugin ampiamente integrato in molti temi WordPress |
Lo sfruttamento di CVE-2026-8206 parte da zero accessi — nessun account, nessuna password, nessuna sessione — fino alla completa compromissione dell'admin utilizzando solo richieste HTTP.
Phase 1: Extract Nonce → Obtain nonce from a public page (if required)
Phase 2: Hijack Reset Email → Send reset link to attacker's email
Phase 3: Reset Password → Change admin password using the obtained reset key
Phase 4: Login Admin → Log in with the new password
Phase 5: Webshell Upload → Install backdoor via plugin upload
Phase 6: RCE → Execute arbitrary commands on the server
Scarica il plugin da wordpress.org e apri direttamente il file CompLibFormHandler.php:
// The handle_forgot_password function receives data from the client:
$email_subject = isset( $form_data['emailSubject'] )
? sanitize_text_field( $form_data['emailSubject'] ) : '';
$email_body = isset( $form_data['emailBody'] )
? json_decode( $form_data['emailBody'], true ) : '';
// Email body rendering logic:
foreach ( $email_body as $body_data ) {
if ( $body_data['type'] === 'text' ) {
$email_body = $email_body . $body_data['value'];
} elseif ( $body_data['type'] === 'chip' ) {
$email_body = $email_body . $chip_data[ $body_data['value'] ];
// ^^^^^^^^^^^^^^^^^^^
// $chip_data['reset_link'] = actual reset URL!
}
}
→ La lettura del codice rivela: emailBody deve essere un array JSON, ogni elemento ha type e value. Se type = chip e value = reset_link, Kirki inserisce automaticamente il link di reset nell'email.
Esegui il grep direttamente nella directory del plugin:
grep -r "emailBody" wp-content/plugins/kirki/assets/js/
grep -r "reset_link" wp-content/plugins/kirki/assets/js/
Il codice frontend compilato rivelerà la struttura JSON esatta richiesta.
Find endpoint (/wp-json/ or rest_route)
↓
Read PHP source → find $form_data['emailBody']
↓
Understand JSON array format (type / value)
↓
Read chip_data array → find key "reset_link"
↓
Build payload: emailBody=[{"type":"chip","value":"reset_link"}]
↓
Test → success
L'attaccante invia una richiesta POST all'endpoint forgot-password con il nome utente della vittima (admin) e un indirizzo email controllato dall'attaccante ([email protected]). Il server genera una chiave di reset per la vittima ma invia il link di reset a [email protected].
Invio della richiesta: