
CVE-2026-16540 — Simply Schedule Appointments < 1.6.12.6 Divulgação de Dados de Agendamento Não Autenticada e Exclusão em Massa
purge| Campo | Detalhes |
|---|---|
| CVE | CVE-2026-16540 |
| Plugin | Simply Schedule Appointments |
| Versões Afetadas | < 1.6.12.6 |
| Corrigido em | 1.6.12.6 |
| Instalações Ativas | ~70.000 |
| CVSS 3.1 | 6.5 (Médio) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N |
| CWE | CWE-863 (Autorização Incorreta) |
| OWASP | A5: Controle de Acesso Quebrado |
| WPVDB | c3829294-c388-4151-9e25-a3eac7b1f1c6 |
| Pesquisador | Suleyman Huseynov (@huseyn0vs__) |
| Divulgado | 2026-07-22 |
O endpoint REST GET /wp-json/ssa/v1/appointments/purge no Simply Schedule Appointments aplica uma verificação de autorização (get_items_permissions_check) que valida um id_token comprovando a propriedade de um único compromisso. No entanto, o manipulador (purge_appointments()) ignora completamente a qual compromisso o token pertence e opera em todos os compromissos em todo o site que correspondem às condições fornecidas.
Um atacante não autenticado que agenda qualquer compromisso individual através do formulário público de agendamento recebe um id_token. Usando esse token, o atacante pode:
A exclusão é em cascata em 6 tabelas de banco de dados dependentes em transações confirmadas, sem funcionalidade nativa de desfazer.
Arquivo: includes/class-appointment-model.php
A verificação de permissão e o manipulador operam em escopos 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' ),
),
) );
Verificação de permissão — valida a propriedade de UM compromisso via 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
}
// ...
}
Manipulador — ignora o compromisso do token e opera em todo o site:
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
}
O id_token é um HMAC-MD5 derivado do id e date_created de um único compromisso. Possuir esse token prova apenas que o chamador agendou um compromisso específico — não confere nenhum privilégio em todo o site.
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)
A PoC será publicada em 2026-08-05 após o embargo de divulgação coordenada.
Consulte o diretório poc/ após a data de embargo.
| Cenário | Resultado |
|---|---|
| Divulgação de dados | Nomes, e-mails, números de telefone, detalhes dos compromissos de todos os clientes |
| Exclusão em massa (premium) | Todos os compromissos históricos/futuros permanentemente destruídos em 6 tabelas |
| Sem caminho de recuperação | Requer restauração completa do backup do banco de dados |
| Data | Evento |
|---|---|
| 2026-07-10 | Vulnerabilidade descoberta |
| 2026-07-10 | Submetido ao WPScan |
| 2026-07-22 | CVE-2026-16540 atribuído |
| 2026-07-22 | Divulgação pública no WPScan |
| 2026-08-05 | PoC publicada |