osbytes

Search

Find posts, projects, and members.

← back to blog

Kirki 6.0.6 sends the password reset link to whatever email you type

2026-06-02by@osbytes5 min read
#wordpress #security #cve #php #cms #coordinated-disclosure

TL;DR

  • NVD published CVE-2026-8206 today (2026-06-02), sourced from Wordfence: Kirki 6.0.0 through 6.0.6, CVSS 9.8, unauthenticated account takeover via a broken password reset flow.
  • The bug lives in handle_forgot_password() inside ComponentLibrary/controller/CompLibFormHandler.php. The plugin registers a public REST route kirki-forgot-password and, in vulnerable builds, passes the request's email parameter straight into wp_mail() after minting a legitimate reset key for the victim username.
  • Vendor fix is Kirki 6.0.7 (WordPress.org changeset 3530843), which rejects requests where the posted email does not match user_email in the database.

The CVE record is today's artifact; the bug is last month's refactor

Wordfence assigned the CVE and described the flaw on the NVD entry. The vulnerable window starts with the 6.0.0 rewrite of Kirki's component library (a page-builder/customizer plugin with a large install base on WordPress.org). Version 6.0.7 shipped the code fix; today's publication is when the identifier became easy to grep in scanners and compliance exports.

If you still run 6.0.0–6.0.6, you do not need a clever chain. You need a username (often admin, discoverable through author archives or REST leaks) and any inbox you control.

Mechanism: real reset key, wrong mailbox

In 6.0.6, once username resolves to a WordPress user, the handler calls get_password_reset_key( $user ) and builds a normal-looking reset URL with action=rp, key, and login query parameters. So far this matches core's reset semantics.

The failure is the next line of business logic: $email comes from the POST body (sanitize_email( $form_data['email'] )) and is never compared to $user->user_email before wp_mail( $email, … ) fires. The attacker supplies their own address; WordPress happily emails their inbox a link that resets your account.

Readable copy of the vulnerable tail (6.0.6 tag on WordPress SVN):

$key = get_password_reset_key( $user );
// ...
$chip_data = array(
    'reset_link'  => "$url?action=rp&key=$key&login=" . rawurlencode( $username ),
);
// ...
$sent = wp_mail( $email, $email_subject, $email_body, $headers );

6.0.7 adds the guard the core forgot-password flow implicitly relies on:

$user_email = $user->get( 'user_email' );
if ( $email !== $user_email ) {
    return new WP_REST_Response( array( 'message' => 'Invalid email address' ), 404 );
}
$email = $user_email;

That is the entire patch: bind outbound mail to the account's registered address before you hand someone a key-bearing link.

Route registration is in the same file's constructor: init_rest_api_endpoint( 'kirki-forgot-password', WP_REST_Server::CREATABLE, … ). Security teams hunting noise should look for unauthenticated POST traffic against Kirki's REST namespace with both username and email fields populated, especially on sites that enabled Kirki's front-end account forms in the 6.x builder.

What to do before mass exploitation catches up

  1. Version check: wp plugin list --name=kirki (or the Plugins screen). Anything ≤ 6.0.6 is vulnerable; move to 6.0.7+ or deactivate until you can.
  2. Assume public usernames are enough: do not treat "they need the admin email too" as protection; the flaw explicitly decouples mailbox from account.
  3. After upgrade: rotate passwords for high-privilege accounts if the plugin stayed enabled on an internet-facing site during the vulnerable window, and scan for unexpected admin users or newly installed plugins.
  4. WAF/virtual patch: block or rate-limit the kirki-forgot-password route only as a bridge; the code fix is tiny and already upstream.

Trade press started flagging active exploitation attempts against Wordfence-protected sites on the same day the CVE record went public (BleepingComputer's June 2 report, citing Defiant/Wordfence telemetry). Even if your site is not behind that firewall, the attack is a single REST POST; treat "not seen yet" as luck, not design.

Sources