Share
## https://sploitus.com/exploit?id=0DCA0C8F-4338-5B4D-B9FB-1FA0A32556B6
# CVE-2026-16540 โ Simply Schedule Appointments WP_REST_Server::READABLE,
'callback' => array( $this, 'purge_appointments' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
),
) );
```
**Permission check** โ validates ownership of ONE appointment via `id_token`:
```php
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
}
// ...
}
```
**Handler** โ ignores the token's appointment and operates site-wide:
```php
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 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
}
```
The `id_token` is an HMAC-MD5 derived from a single appointment's `id` and `date_created`. Possessing this token proves only that the caller booked one specific appointment โ it conveys no site-wide privilege whatsoever.
---
## Attack Chain
```
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=
&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)
```
---
## Proof of Concept
> **PoC will be published on 2026-08-05** following the coordinated disclosure embargo.
See [`poc/`](./poc/) directory after the embargo date.
---
## Impact
| Scenario | Result |
|----------|--------|
| Data disclosure | Names, emails, phone numbers, appointment details of all customers |
| Mass deletion (premium) | All historical/future appointments permanently destroyed across 6 tables |
| No recovery path | Requires full database backup restore |
---
## Remediation
The fix should scope the `purge_appointments()` handler to only operate on appointments belonging to the authenticated token's owner, or restrict the endpoint to administrators only:
```php
// Option 1: Restrict to admins only
'permission_callback' => function( $request ) {
return current_user_can( 'ssa_manage_site_settings' );
}
// Option 2: Scope deletion to token owner's appointment only
$customer_id = $this->get_customer_id_from_token( $request );
$conditions[] = $wpdb->prepare( 'customer_id = %d', $customer_id );
```
**Fixed in version 1.6.12.6.**
---
## Disclosure Timeline
| Date | Event |
|------|-------|
| 2026-07-?? | Vulnerability discovered |
| 2026-07-22 | Submitted to WPScan |
| 2026-07-22 | CVE-2026-16540 assigned |
| 2026-07-22 | Public disclosure on WPScan |
| 2026-08-05 | PoC published |
---
## References
- [WPScan Advisory](https://wpscan.com/vulnerability/c3829294-c388-4151-9e25-a3eac7b1f1c6)
- [WordPress Plugin Page](https://wordpress.org/plugins/simply-schedule-appointments/)
- [CWE-863: Incorrect Authorization](https://cwe.mitre.org/data/definitions/863.html)
- [OWASP A5: Broken Access Control](https://owasp.org/Top10/A01_2021-Broken_Access_Control/)