
CVE-2026-16540 — Simply Schedule Appointments < 1.6.12.6. Неавторизованное раскрытие данных записей и массовое удаление.
purge| Поле | Подробности |
|---|---|
| CVE | CVE-2026-16540 |
| Плагин | Simply Schedule Appointments |
| Затронутые версии | < 1.6.12.6 |
| Исправлено в | 1.6.12.6 |
| Активных установок | ~70,000 |
| CVSS 3.1 | 6.5 (Средний) — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N |
| CWE | CWE-863 (Неверная авторизация) |
| OWASP | A5: Нарушение контроля доступа |
| WPVDB | c3829294-c388-4151-9e25-a3eac7b1f1c6 |
| Исследователь | Suleyman Huseynov (@huseyn0vs__) |
| Раскрыто | 2026-07-22 |
Конечная точка REST GET /wp-json/ssa/v1/appointments/purge в Simply Schedule Appointments применяет проверку авторизации (get_items_permissions_check), которая проверяет id_token, подтверждающий владение одной записью. Однако обработчик (purge_appointments()) полностью игнорирует, какой записи принадлежит токен, и вместо этого оперирует всеми записями на сайте, соответствующими предоставленным условиям.
Неаутентифицированный злоумышленник, записавшись на любую одну услугу через общедоступную форму, получает id_token. Используя этот токен, злоумышленник может:
Удаление каскадно распространяется на 6 связанных таблиц базы данных в зафиксированных транзакциях, без встроенной возможности отмены.
Файл: includes/class-appointment-model.php
Проверка прав доступа и обработчик оперируют совершенно разными областями:
// 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' ),
),
) );
Проверка прав — проверяет владение ОДНОЙ записью через 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
}
// ...
}
Обработчик — игнорирует запись токена и работает на уровне всего сайта:
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
}
id_token — это HMAC-MD5, производный от id и date_created одной записи. Владение этим токеном доказывает только то, что вызывающий записался на конкретную услугу — он не даёт никаких привилегий на уровне сайта.
1. Злоумышленник посещает страницу записи и записывается на любую бесплатную услугу
→ получает письмо с подтверждением, содержащее: appointment_id + id_token
2. GET /wp-json/ssa/v1/appointments/purge
?id=<appointment_id>
&token=<id_token>
&purge_past_appointments=true
3. Проверка прав проходит (действительный токен для собственной записи злоумышленника)
4. Обработчик выбирает ВСЕ прошлые записи на сайте → возвращает PII в ответе
→ навсегда удаляет их из 6 таблиц (в премиум-версии)
PoC будет опубликован 2026-08-05 после окончания эмбарго по согласованному раскрытию.
Смотрите каталог poc/ после даты эмбарго.
| Сценарий | Результат |
|---|---|
| Раскрытие данных | Имена, email, номера телефонов, детали записей всех клиентов |
| Массовое удаление (премиум) | Все исторические/будущие записи безвозвратно уничтожены в 6 таблицах |
| Нет пути восстановления | Требуется восстановление полной резервной копии базы данных |
| Дата | Событие |
|---|---|
| 2026-07-10 | Уязвимость обнаружена |
| 2026-07-10 | Отправлено в WPScan |
| 2026-07-22 | Присвоен CVE-2026-16540 |
| 2026-07-22 | Публичное раскрытие на WPScan |
| 2026-08-05 | Опубликован PoC |