WP Time Slots Booking Form Unauthenticated Stored XSS (CVE-2026-40791)
by Daniel Wade (Rat5ak / Nadsec) · 2026-07-05
- Severity
- High
- CVE
- CVE-2026-40791
- Category
- web
- Affected product
- WP Time Slots Booking Form (wp-time-slots-booking-form WordPress plugin)
- Affected versions
- <= 1.2.46 (fixed in 1.2.47)
- Disclosed
- 2026-07-05
- Patch status
- patched
Archive entry
intelseclab/poc-archiveMetadata
| Field | Value |
|---|---|
| Date Added | 2026-07-05 |
| Last Updated | 2026-04 |
| Author / Researcher | Daniel Wade (Rat5ak / Nadsec) |
| CVE / Advisory | CVE-2026-40791 |
| Category | web |
| Severity | High |
| CVSS Score | 7.2 (Wordfence), 7.1 (Patchstack) |
| Status | PoC |
| Tags | wordpress, wordpress-plugin, stored-xss, unauthenticated, cwe-79, admin-takeover, booking-form |
| Related | N/A |
Affected Target
| Field | Value |
|---|---|
| Software / System | WP Time Slots Booking Form (wp-time-slots-booking-form WordPress plugin) |
| Versions Affected | <= 1.2.46 (fixed in 1.2.47) |
| Language / Platform | PHP / WordPress; PoC in Bash and PowerShell |
| Authentication Required | No (unauthenticated submitter); admin only needs to view a page |
| Network Access Required | Yes |
Summary
The public booking form of the WP Time Slots Booking Form plugin parses a submitted appointment field by splitting on a literal space character, then stores the resulting substring as the booking’s time-slot value. Because HTML treats a tab character as valid whitespace between an SVG tag and its attribute, an attacker can submit a payload like 8:<svg[TAB]onload=alert(document.cookie)> — PHP’s explode(' ', ...) keeps it as a single token (since it only splits on spaces, not tabs), but the browser still parses it as a real <svg onload=...> element. That value is serialized and later printed, unescaped, into the administrator’s Booking Orders page (a phpcs:ignore WordPress.Security.EscapeOutput comment marks the exact line where escaping was suppressed). When an administrator views Booking Orders, the attacker’s JavaScript executes in the authenticated wp-admin origin.
Vulnerability Details
Root Cause
extract_appointments() splits the submitted appointment string with explode(' ', $app_item_text) and stores $item_split[1] as the slot value without validation; the admin-facing renderer in cp-admin-int-message-list.inc.php then concatenates that stored value directly into HTML output with escaping explicitly suppressed via a phpcs:ignore comment.
Attack Vector
- Submit an unauthenticated POST to the plugin’s public booking form endpoint with the appointment field crafted as
2026-04-15 8:<svg[TAB]onload=alert(document.cookie)> 1 0 0 0 0 0 0(tab character betweensvgandonload). - The plugin’s space-based parser preserves the tab-separated SVG tag intact as the stored “slot” value and serializes it into
wp_cptslotsbk_messages.posted_data. - An administrator opens the plugin’s Booking Orders admin page, which unserializes and prints the stored slot value without HTML-escaping it.
- The embedded
<svg onload=...>executes in the administrator’s browser, in the wp-admin origin.
Impact
Unauthenticated stored XSS that executes in an administrator’s authenticated wp-admin session; while HttpOnly may protect the raw auth cookie, the script can still make same-origin authenticated requests (reading nonces, submitting admin POSTs), enabling site takeover actions such as creating users or changing plugin/site configuration.
Environment / Lab Setup
Target: WordPress + WP Time Slots Booking Form <= 1.2.46, public booking page (e.g. page_id with [CP_TIME_SLOTS_BOOKING id="1"])
Attacker: bash+curl or PowerShell (see poc/reproduce.sh, poc/reproduce.ps1); admin browser to observe execution
Proof of Concept
PoC Script
See
poc/reproduce.sh,poc/reproduce.ps1, andlab/render-check.html/lab/validate-render.ps1in this folder.
| |
The script submits an unauthenticated booking to the target’s public booking form with the tab-separated SVG payload embedded in the appointment field, expecting an HTTP 302 response confirming acceptance. lab/validate-render.ps1 independently verifies the two mechanical parts of the bug (that the PHP-style space-split preserves the tabbed SVG, and that a browser executes the resulting markup) without requiring a full WordPress install.
Detection & Indicators of Compromise
Signs of compromise:
- Stored booking rows in
wp_cptslotsbk_messagescontaining HTML tags in the slot/time field - Unexpected admin session activity (new users, changed settings) shortly after an admin viewed Booking Orders
<svg onload=...>or similar markup appearing in booking-related database entries
Remediation
| Action | Detail |
|---|---|
| Primary fix | Update WP Time Slots Booking Form to 1.2.47 or later, which applies esc_html() to the stored slot value before rendering |
| Interim mitigation | Disable the plugin’s public booking form or restrict admin access to Booking Orders until patched; a WAF rule blocking tab characters adjacent to < in form submissions can reduce exposure |
References
Notes
Mirrored from https://github.com/Rat5ak/CVE-2026-40791-WP-Time-Slots-Booking-Form-XSS on 2026-07-05.