
CVE-2026-16540 — Simply Schedule Appointments < 1.6.12.6 Divulgación no autenticada de datos de citas y eliminación masiva
purge| Campo | Detalles |
|---|---|
| CVE | CVE-2026-16540 |
| Plugin | Simply Schedule Appointments |
| Versiones afectadas | < 1.6.12.6 |
| Corregido en | 1.6.12.6 |
| Instalaciones activas | ~70,000 |
| CVSS 3.1 | 6.5 (Medio) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N |
| CWE | CWE-863 (Autorización incorrecta) |
| OWASP | A5: Control de acceso roto |
| WPVDB | c3829294-c388-4151-9e25-a3eac7b1f1c6 |
| Investigador | Suleyman Huseynov (@huseyn0vs__) |
| Divulgado | 2026-07-22 |
El endpoint REST GET /wp-json/ssa/v1/appointments/purge en Simply Schedule Appointments aplica una comprobación de autorización (get_items_permissions_check) que valida un id_token que demuestra la propiedad de una sola cita. Sin embargo, el manejador (purge_appointments()) ignora por completo a qué cita pertenece el token y, en su lugar, opera sobre todas las citas de todo el sitio que coincidan con las condiciones proporcionadas.
Un atacante no autenticado que reserva una sola cita a través del formulario público de reserva recibe un id_token. Usando ese token, el atacante puede:
La eliminación se propaga en cascada a través de 6 tablas de base de datos dependientes en transacciones confirmadas, sin funcionalidad nativa de deshacer.
Archivo: includes/class-appointment-model.php
La puerta de permisos y el manejador operan en ámbitos completamente diferentes:
// Route registration
register_rest_route( $namespace, '/' . $base . '/purge', array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'purge_appointments' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
),
) );
Comprobación de permisos — valida la propiedad de UNA cita mediante id_token:
public function get_items_permissions_check( $request ) {
// ...
if ( true === $this->id_token_permissions_check( $request ) ) {
return true; // grants access if caller proves ownership of one appointment
}
// ...
}
Manejador — ignora la cita del token y opera en todo el sitio:
public function purge_appointments( WP_REST_Request $request ) {
$params = $request->get_params();
// No customer_id or appointment_id scoping — deletes ALL matching appointments
if ( isset( $params['purge_past_appointments'] ) && 'true' === $params['purge_past_appointments'] ) {
$conditions[] = $wpdb->prepare( 'end_date < %s', $date_modified_max->format( 'Y-m-d' ) );
}
$sql = 'SELECT * FROM ' . $this->get_table_name()
. ' WHERE ' . implode( ' OR ', $conditions )
. ' ORDER BY id ASC LIMIT 5000';
$list = $wpdb->get_results( $sql, ARRAY_A );
// Cascade-deletes across 6 tables in committed transactions
}
El id_token es un HMAC-MD5 derivado del id y date_created de una sola cita. Poseer este token solo demuestra que quien lo presenta reservó una cita específica; no confiere ningún privilegio a nivel de sitio.
1. Attacker visits the booking page and books any free appointment
→ receives confirmation email containing: appointment_id + id_token
2. GET /wp-json/ssa/v1/appointments/purge
?id=<appointment_id>
&token=<id_token>
&purge_past_appointments=true
3. Permission check passes (valid token for attacker's own appointment)
4. Handler selects ALL past appointments site-wide → returns PII in response
→ permanently deletes them across 6 tables (on premium editions)
El PoC se publicará el 2026-08-05 tras el embargo de divulgación coordinada.
Consulta el directorio poc/ después de la fecha de embargo.
| Escenario | Resultado |
|---|---|
| Divulgación de datos | Nombres, correos electrónicos, números de teléfono y detalles de las citas de todos los clientes |
| Eliminación masiva (premium) | Todas las citas históricas/futuras destruidas permanentemente en 6 tablas |
| Sin vía de recuperación | Requiere restaurar una copia de seguridad completa de la base de datos |
| Fecha | Evento |
|---|---|
| 2026-07-10 | Vulnerabilidad descubierta |
| 2026-07-10 | Enviada a WPScan |
| 2026-07-22 | CVE-2026-16540 asignado |
| 2026-07-22 | Divulgación pública en WPScan |
| 2026-08-05 | PoC publicado |