CVE-2026-40791

WP Time Slots Booking Form <= 1.2.46 - Unauthenticated Stored Cross-Site Scripting

highImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
7.2
CVSS Score
7.2
CVSS Score
high
Severity
1.2.47
Patched in
8d
Time to patch

Description

The WP Time Slots Booking Form plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.2.46 due to insufficient input sanitization and output escaping. This makes it possible for unauthenticated attackers to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.2.46
PublishedApril 23, 2026
Last updatedApril 30, 2026

What Changed in the Fix

Changes introduced in v1.2.47

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan targets **CVE-2026-40791**, an unauthenticated stored Cross-Site Scripting (XSS) vulnerability in the **WP Time Slots Booking Form** plugin. ### 1. Vulnerability Summary The plugin fails to sanitize user-supplied data during the booking submission process and fails to escape that…

Show full research plan

This research plan targets CVE-2026-40791, an unauthenticated stored Cross-Site Scripting (XSS) vulnerability in the WP Time Slots Booking Form plugin.

1. Vulnerability Summary

The plugin fails to sanitize user-supplied data during the booking submission process and fails to escape that data when displaying it in the administrative "Booking Orders" list. An unauthenticated attacker can submit a booking request containing a malicious script in any form field. When an administrator views the bookings for that calendar, the script executes in their session context.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: cp_tslotsbooking_submit (Inferred from class prefix cp_tslotsbooking in cp-main-class.inc.php).
  • Vulnerable Parameter: Any form field submitted via POST (e.g., email, or additional custom fields like fieldname2).
  • Authentication: None required (unauthenticated).
  • Preconditions: A booking form (calendar) must be published on a public page to obtain a valid submission nonce and the calendar ID (cal).

3. Code Flow

  1. Submission (Source): An unauthenticated user submits a booking via AJAX. The plugin handles this in cp-main-class.inc.php (inside a method typically named submit or process_submission hooked to wp_ajax_nopriv_cp_tslotsbooking_submit).
  2. Storage: The plugin iterates through $_POST data, serializes it, and inserts it into the $wpdb->prefix . "cptslotsbk_messages" table in the posted_data column.
  3. Retrieval: An admin navigates to the "Booking Orders" page. cp-admin-int-message-list.inc.php is loaded.
  4. Processing:
    • Line 116-121: The code fetches records from $this->table_messages (which is cptslotsbk_messages).
    • Line 144: The query results are stored in $events.
    • Line 70-76 (Example of processing): The code calls unserialize($myrows[0]->posted_data).
  5. Rendering (Sink): Although truncated in the snippet, the plugin typically iterates through the deserialized posted_data and echoes the keys and values into a table for the administrator. Because no esc_html() or wp_kses() is applied during this output, the XSS triggers.

4. Nonce Acquisition Strategy

The booking submission requires a nonce generated for the specific form. This nonce is tied to the uid=0 (anonymous user) session.

  1. Identify Shortcode: The default shortcode is [CP_TIME_SLOTS_BOOKING].
  2. Create Test Page:
    wp post create --post_type=page --post_status=publish --post_title="Booking Test" --post_content='[CP_TIME_SLOTS_BOOKING id="1"]'
    
  3. Navigate and Extract:
    Use browser_navigate to visit the new page.
    Use browser_eval to extract the nonce from the localized JavaScript configuration object.
    • Variable Name: cp_tslotsbooking_fbuilder_config
    • Nonce Path: cp_tslotsbooking_fbuilder_config.obj.nonce
    • Item ID: cp_tslotsbooking_fbuilder_config.obj.item_id (This confirms the cal ID).

5. Exploitation Strategy

The exploit involves a single POST request to the AJAX endpoint using the extracted nonce.

HTTP Request:

  • Method: POST
  • URL: http://<target>/wp-admin/admin-ajax.php?action=cp_tslotsbooking_submit
  • Content-Type: application/x-www-form-urlencoded
  • Parameters:
    • cp_tslotsbooking_nonce: [EXTRACTED_NONCE]
    • item: 1 (The calendar ID)
    • fieldname1_date: 2025-12-25 (Required for valid booking)
    • fieldname1_slot: 08:00 - 09:00 (Required for valid booking)
    • email: admin@victim.com"><script>alert(document.domain)</script>
    • fieldname2: <img src=x onerror=alert("XSS")> (Additional field often stored)

6. Test Data Setup

  1. Ensure the plugin is activated.
  2. The default "Form 1" (ID 1) is created upon installation (as seen in _install in cp-main-class.inc.php).
  3. Create a public page with the shortcode [CP_TIME_SLOTS_BOOKING id="1"].
  4. Ensure no Captcha is blocking the request (the default is true, so the PoC may need to disable it via WP-CLI: wp option update cp_tslotsbooking_cv_enable_captcha 'false').

7. Expected Results

  • The admin-ajax.php response should indicate a successful submission (usually a redirect URL or a JSON success message).
  • When an admin visits /wp-admin/admin.php?page=cp_timeslotsbooking&cal=1&acc=messages, the JavaScript payload will execute.

8. Verification Steps

After sending the HTTP request, verify the storage via WP-CLI:

# Check the cptslotsbk_messages table for the payload
wp db query "SELECT posted_data FROM wp_cptslotsbk_messages ORDER BY id DESC LIMIT 1"

The output should contain the serialized XSS string.

9. Alternative Approaches

If fieldname1 submission is too complex due to date/time logic:

  • Inject via Search: cp-admin-int-message-list.inc.php line 116 uses $_GET["search"] in a SQL query but then likely reflects it in the "Results for: ..." text. Test admin.php?page=cp_timeslotsbooking&cal=1&acc=messages&search=<script>alert(1)</script>.
  • Status Update: If an unauthenticated user can trigger a status update (unlikely due to admin checks), the status parameter in update_status might be a sink.
  • Manual Update Sink: Line 72 uses $_GET["status"] to update posted_data via the lu (load update) parameter. If this can be reached, it's an alternative storage vector.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Time Slots Booking Form plugin is vulnerable to unauthenticated stored Cross-Site Scripting (XSS) due to a failure to sanitize input and escape output during the booking submission and display process. Attackers can inject malicious scripts into booking fields, which are then executed in the context of an administrator's session when they view the booking orders list.

Vulnerable Code

// cp-main-class.inc.php around line 1481
'date' => $item_split[0],
'slot' => $item_split[1],

---

// cp-admin-int-message-list.inc.php around line 415
$appts .=   '<div class="ahb-appointment-badge">' .
               '<span class="dashicons dashicons-clock"></span>' .
               '<span class="ahb-time">'.$this->format_date($posted_data["apps"][$k]["date"]).' '.$posted_data["apps"][$k]["slot"].'</span>' .

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-time-slots-booking-form/1.2.46/cp-admin-int-message-list.inc.php /home/deploy/wp-safety.org/data/plugin-versions/wp-time-slots-booking-form/1.2.47/cp-admin-int-message-list.inc.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-time-slots-booking-form/1.2.46/cp-admin-int-message-list.inc.php	2026-03-27 17:30:42.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-time-slots-booking-form/1.2.47/cp-admin-int-message-list.inc.php	2026-04-10 09:44:46.000000000 +0000
@@ -412,7 +412,7 @@
     $app = $posted_data["apps"][$k];
     $appts .=   '<div class="ahb-appointment-badge">' .
                    '<span class="dashicons dashicons-clock"></span>' .
-                   '<span class="ahb-time">'.$this->format_date($posted_data["apps"][$k]["date"]).' '.$posted_data["apps"][$k]["slot"].'</span>' .
+                   '<span class="ahb-time">'.$this->format_date($posted_data["apps"][$k]["date"]).' '.esc_html($posted_data["apps"][$k]["slot"]).'</span>' .
                    '<span class="ahb-service">'.($app["quantity1"] + $app["quantity2"] + $app["quantity3"] + $app["quantity4"] + $app["quantity5"] > 1 || $app["quantity1"] == 0 ? 
 				      " (".(
                       $this->getQuantityLabel("quantity1",$app["field"],$formdata).$app["quantity1"]
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-time-slots-booking-form/1.2.46/cp-main-class.inc.php /home/deploy/wp-safety.org/data/plugin-versions/wp-time-slots-booking-form/1.2.47/cp-main-class.inc.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-time-slots-booking-form/1.2.46/cp-main-class.inc.php	2026-03-27 17:30:42.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-time-slots-booking-form/1.2.47/cp-main-class.inc.php	2026-04-10 09:44:46.000000000 +0000
@@ -1478,8 +1478,8 @@
                                      'cancelled' => $this->get_option('defaultstatus', ''),
                                      'baseprice' => floatval($server_side_price),
                                      'price' => floatval($server_side_price),
-                                     'date' => $item_split[0],
-                                     'slot' => $item_split[1],
+                                     'date' => sanitize_text_field($item_split[0]),
+                                     'slot' => sanitize_text_field($item_split[1]),
                                      'quantity1' => $quantity1,
                                      'quantity2' => $quantity2,
                                      'quantity3' => $quantity3,

Exploit Outline

1. Identify a public page on the target site containing a booking form (shortcode [CP_TIME_SLOTS_BOOKING]). 2. Extract the required submission nonce and item (calendar) ID from the localized JavaScript object `cp_tslotsbooking_fbuilder_config` in the page source. 3. Construct a POST request to the WordPress AJAX endpoint `/wp-admin/admin-ajax.php?action=cp_tslotsbooking_submit`. 4. In the request body, include the extracted nonce and a payload for a slot-related field (e.g., `fieldname1_slot`) containing a malicious script tag like `<script>alert(document.domain)</script>`. 5. Wait for an administrator to log in and navigate to the 'Booking Orders' page (accessible via the plugin menu) for that specific calendar ID. 6. The script will execute automatically when the admin dashboard renders the appointment list containing the malicious payload.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.