Appointment Booking Calendar <= 1.6.9.29 - Insecure Direct Object Reference to Authenticated (Staff+) Sensitive Information Exposure
Description
The Appointment Booking Calendar — Simply Schedule Appointments Booking Plugin plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 1.6.9.29. This is due to the `get_item_permissions_check` method granting access to users with the `ssa_manage_appointments` capability without validating staff ownership of the requested appointment. This makes it possible for authenticated attackers, with custom-level access and above (users granted the ssa_manage_appointments capability, such as Team Members), to view appointment records belonging to other staff members and access sensitive customer personally identifiable information via the appointment ID parameter.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:NTechnical Details
<=1.6.9.29What Changed in the Fix
Changes introduced in v1.6.10.0
Source Code
WordPress.org SVNThis plan outlines the research and exploitation process for **CVE-2026-1704**, an Insecure Direct Object Reference (IDOR) vulnerability in the "Simply Schedule Appointments" plugin. ### 1. Vulnerability Summary The vulnerability exists in the plugin's REST API implementation for retrieving appoint…
Show full research plan
This plan outlines the research and exploitation process for CVE-2026-1704, an Insecure Direct Object Reference (IDOR) vulnerability in the "Simply Schedule Appointments" plugin.
1. Vulnerability Summary
The vulnerability exists in the plugin's REST API implementation for retrieving appointment details. Specifically, the get_item_permissions_check method (likely within SSA_Appointments_Api, inferred from plugin structure) grants access to any user with the ssa_manage_appointments capability.
While this capability is intended for staff/team members, the implementation fails to verify if the requesting staff member is actually assigned to or owns the specific appointment requested via the id parameter. This allows a low-privileged "Team Member" to view sensitive customer Personally Identifiable Information (PII) for appointments belonging to any other staff member.
2. Attack Vector Analysis
- Endpoint:
/wp-json/ssa/v1/appointments/(?P<id>\d+)(Inferred based onSSA_Settings_Apipatterns inincludes/class-settings-api.php). - HTTP Method:
GET - Vulnerable Parameter:
id(The appointment ID). - Authentication Required: Authenticated user with the
ssa_manage_appointmentscapability. - Required Role: Typically a user with a "Team Member" role or a custom role granted SSA capabilities.
- Precondition: The "Team Booking" feature must be enabled or the system must have multiple staff members and existing appointments.
3. Code Flow
- Request Entry: The attacker sends a
GETrequest to/wp-json/ssa/v1/appointments/{id}. - Routing: WordPress matches the route registered via
register_rest_route. - Permission Check: The controller calls
get_item_permissions_check($request). - Flawed Logic: The method checks
current_user_can('ssa_manage_appointments'). Iftrue, it returnstrue. It does not query theSSA_Appointment_Modelto verify if the$request['id']is associated with thecurrent_user_id. - Data Retrieval: The controller calls
get_item( $request ), which invokesSSA_Appointment_Model::db_get( $id ). - Information Exposure: The response returns the full appointment record, including the
customer_informationarray containing PII (name, email, phone, etc.).
4. Nonce Acquisition Strategy
SSA uses the standard WordPress REST API nonce (wp_rest). This nonce is required for all authenticated ssa/v1 requests.
- Identify Trigger: The SSA Admin dashboard (
/wp-admin/admin.php?page=ssa-settings) loads the main booking application which enqueues the necessary scripts and nonces. - Setup Page: A page containing the SSA admin app is needed. Usually, simply logging in as a user with
ssa_manage_appointmentsand visiting the SSA dashboard is sufficient. - Navigation: Use
browser_navigatetotarget_url + "/wp-admin/admin.php?page=ssa-settings". - Extraction: SSA localizes its data into a global JavaScript object. Based on common plugin patterns:
- Variable:
window.SSA_Dataorwindow.ssa_admin. - Command:
browser_eval("window.SSA_Data?.rest_nonce || window.ssa_admin?.nonce"). - Alternative: Extract the standard
wp-jsonnonce from thewp-api-fetchinitialization or the_wpnoncein the page source.
- Variable:
5. Test Data Setup
To demonstrate the vulnerability, we need two staff members and one appointment.
- Create Staff 1 (Victim):
wp user create victim victim@example.com --role=author --user_pass=password # Grant SSA capability (usually handled by the plugin, but we can force it) wp cap add author ssa_manage_appointments - Create Staff 2 (Attacker):
wp user create attacker attacker@example.com --role=author --user_pass=password wp cap add author ssa_manage_appointments - Create Appointment for Victim:
Usewp evalto insert directly into the SSA appointments table (table name prefix:wp_ssa_appointments).$plugin = Simply_Schedule_Appointments::get_instance(); $appointment_id = $plugin->appointment_model->insert(array( 'appointment_type_id' => 1, 'customer_information' => array( 'first_name' => 'Secret', 'last_name' => 'Customer', 'email' => 'sensitive@victim.com', 'phone' => '555-0199' ), 'status' => 'booked', 'staff_ids' => array( [ID_OF_VICTIM_USER] ) )); echo "Created Appointment ID: " . $appointment_id;
6. Exploitation Strategy
- Login: Log in as
attacker. - Extract Nonce: Navigate to
/wp-admin/admin.php?page=ssa-settingsand extract thewp_restnonce. - Perform IDOR: Use the
http_requesttool to fetch an appointment ID belonging to thevictim.
HTTP Request:
GET /wp-json/ssa/v1/appointments/[VICTIM_APPOINTMENT_ID] HTTP/1.1
Host: localhost:8080
X-WP-Nonce: [EXTRACTED_NONCE]
Cookie: [ATTACKER_COOKIES]
7. Expected Results
- Vulnerable Version: The server returns a
200 OKresponse with a JSON body containing thecustomer_informationfield with the victim's customer's PII. - Patched Version: The server returns a
403 Forbiddenor401 Unauthorizedbecause the requester is not the assigned staff member.
8. Verification Steps
- Analyze Response: Inspect the JSON output for keys:
first_name,last_name,email, andphone. - Verify Ownership: Use WP-CLI to confirm the appointment belongs to the victim and not the attacker:
# Check staff_ids for the appointment wp db query "SELECT staff_ids FROM wp_ssa_appointments WHERE id = [ID]"
9. Alternative Approaches
- List Appointments: If the single item endpoint is protected, check the collection endpoint:
GET /wp-json/ssa/v1/appointments. Does it filter by the current user? If not, it's a "Mass Assignment" or "Information Exposure" vulnerability. - Capability Level: Test if lower roles (subscriber) can access the endpoint if the capability check is entirely missing or incorrectly implemented.
- Meta Model: Check
ssa/v1/appointment-meta(inferred from propertyappointment_meta_model) for similar IDOR vulnerabilities.
Summary
The Simply Schedule Appointments plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) because its REST API permission checks fail to verify appointment ownership for staff members. Authenticated attackers with the 'ssa_manage_appointments' capability (such as Team Members) can exploit this to view sensitive customer personally identifiable information (PII) belonging to appointments assigned to other staff members.
Vulnerable Code
// includes/class-appointment-model.php around line 1442 if ( current_user_can( 'ssa_manage_appointments' ) ) { return true; }
Security Fix
@@ -1,5 +1,13 @@ # Changelog +## SSA-VERSION-PREFIX.6.10.0 - 2026-03-05 + +### Fixes + +- Vulnerability - Insecure Direct Object Reference to Authenticated (Staff+) Sensitive Information Exposure +- Add Authorization to Settings REST API Endpoint +- Unauthenticated SQL Injection in Simply Schedule Appointments Plugin + ## SSA-VERSION-PREFIX.6.9.29 - 2026-02-24 ### Fixes @@ -1440,7 +1440,10 @@ } if ( current_user_can( 'ssa_manage_appointments' ) ) { - return true; + $params = $request->get_params(); + if ( ! empty( $params['id'] ) && $this->plugin->staff_appointment_model->user_has_appointment_id( get_current_user_id(), (int) $params['id'] ) ) { + return true; + } } $params = $request->get_params(); @@ -128,6 +128,7 @@ */ public function get_item( $request ) { $settings = $this->plugin->settings->get(); + $settings = $this->plugin->settings->remove_unauthorized_settings_for_current_user( $settings ); if ( empty( $settings[$request['id']] ) ) { return array( 'response_code' => 404, @@ -1181,7 +1181,15 @@ $sanitized_orderby = sanitize_key(esc_sql( $args['orderby'] )); $sanitized_order = 'ASC' === strtoupper( esc_sql( $args['order'] ) ) ? 'ASC' : 'DESC'; $table_name = $this->get_table_name(); - $fields = empty( $args['fields'] ) ? '*' : '`' . implode( '`, `', $args['fields'] ) . '`'; + + if ( empty( $args['fields'] ) ) { + $fields = '*'; + } else { + $valid_fields = array_keys( $this->get_fields() ); + $requested = array_map( 'sanitize_key', (array) $args['fields'] ); + $safe_fields = array_intersect( $requested, $valid_fields ); + $fields = empty( $safe_fields ) ? '*' : '`' . implode( '`, `', $safe_fields ) . '`'; + }
Exploit Outline
1. Authenticate as a user with the 'ssa_manage_appointments' capability (typically a Team Member or custom role). 2. Navigate to the SSA settings page in the WordPress admin dashboard to obtain a valid REST API nonce (extractable from JS variables like `window.SSA_Data.rest_nonce`). 3. Identify the ID of an appointment assigned to a different staff member. 4. Send an authenticated GET request to `/wp-json/ssa/v1/appointments/{id}` including the REST nonce in the `X-WP-Nonce` header. 5. Observe that the response contains the full appointment record, including sensitive customer details (name, email, phone) regardless of who the appointment is assigned to.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.