CVE-2026-1856

Appointment Booking Calendar <= 1.4.4 - Authenticated (Author+) Stored Cross-Site Scripting via Custom Booking Field Label

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
1.4.5
Patched in
1d
Time to patch

Description

The Appointment Booking Calendar plugin for WordPress is vulnerable to Stored Cross-Site Scripting via custom booking field labels in all versions up to, and including, 1.4.4 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with Author-level access and above, 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:L/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.4.4
PublishedJune 18, 2026
Last updatedJune 19, 2026
Affected plugincreavi-booking-service

What Changed in the Fix

Changes introduced in v1.4.5

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the technical steps to exploit **CVE-2026-1856**, a Stored Cross-Site Scripting (XSS) vulnerability in the **Appointment Booking Calendar** plugin (version <= 1.4.4). ### 1. Vulnerability Summary The vulnerability exists in how the plugin handles custom booking field lab…

Show full research plan

This research plan outlines the technical steps to exploit CVE-2026-1856, a Stored Cross-Site Scripting (XSS) vulnerability in the Appointment Booking Calendar plugin (version <= 1.4.4).

1. Vulnerability Summary

The vulnerability exists in how the plugin handles custom booking field labels. While the plugin uses sanitize_text_field() during the saving process in includes/save-service.php, this function only strips HTML tags (like <script>) and does not escape quotes or other characters that can be used for attribute injection. Furthermore, the frontend JavaScript (assets/js/booking.js) and PHP rendering logic (includes/render-booking-inline.php) do not sufficiently escape these labels when constructing the booking form's HTML. An attacker with Author-level permissions can inject a payload into a field label that executes arbitrary JavaScript in the context of any user viewing the booking form.

2. Attack Vector Analysis

  • Vulnerable Endpoint: WordPress Admin Post Editor (wp-admin/post.php).
  • Vulnerable Action: Saving or updating a creavibc_service post type.
  • Vulnerable Parameter: creavibc_custom_fields[label][].
  • Authentication Requirement: Author-level access or higher.
  • Preconditions: The attacker must have permission to create or edit creavibc_service posts.

3. Code Flow

  1. **Input
Research Findings
Static analysis — not yet PoC-verified

Summary

The Appointment Booking Calendar plugin is vulnerable to Stored Cross-Site Scripting via custom booking field labels due to a failure to escape labels when rendering them in the frontend booking form. Authenticated attackers with Author-level permissions or higher can inject arbitrary JavaScript that executes whenever a user views the affected booking service form.

Vulnerable Code

// includes/save-service.php line 133
$custom_fields = isset($_POST['creavibc_custom_fields']) ? wp_unslash($_POST['creavibc_custom_fields']) : [];
$labels   = isset($custom_fields['label']) ? array_map('sanitize_text_field', $custom_fields['label']) : [];
$types    = isset($custom_fields['type']) ? array_map('sanitize_text_field', $custom_fields['type']) : [];
$required = isset($custom_fields['required']) ? array_map('sanitize_text_field', $custom_fields['required']) : [];

---

// assets/js/booking.js line 782
if (Array.isArray(form.custom)) {
    form.custom.forEach((field, i) => {
        //const label = field.label || `Field ${i + 1}`;
        const label = field.label || sprintf( __( 'Field %d', CBS_DOMAIN ), i + 1 );

        const type = field.type || 'text';
        const required = field.required ? 'required' : '';
        html += `<label>${label}${required ? ' <span class="creavibc-required">*</span>' : ''}`;
        html += type === 'textarea'
            ? `<textarea id="creavibc-custom-${i}-${serviceId}" data-label="${label}" ${required}></textarea>`
            : `<input type="text" id="creavibc-custom-${i}-${serviceId}" data-label="${label}" ${required}>`;
        html += `</label>`;
    });
}

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/creavi-booking-service/1.4.4/assets/js/booking.js /home/deploy/wp-safety.org/data/plugin-versions/creavi-booking-service/1.4.5/assets/js/booking.js
--- /home/deploy/wp-safety.org/data/plugin-versions/creavi-booking-service/1.4.4/assets/js/booking.js	2026-05-26 11:37:32.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/creavi-booking-service/1.4.5/assets/js/booking.js	2026-06-08 09:55:54.000000000 +0000
@@ -779,20 +779,21 @@
 
         }
 
-        if (Array.isArray(form.custom)) {
-            form.custom.forEach((field, i) => {
-                //const label = field.label || `Field ${i + 1}`;
-                const label = field.label || sprintf( __( 'Field %d', CBS_DOMAIN ), i + 1 );
-
-                const type = field.type || 'text';
-                const required = field.required ? 'required' : '';
-                html += `<label>${label}${required ? ' <span class="creavibc-required">*</span>' : ''}`;
-                html += type === 'textarea'
-                    ? `<textarea id="creavibc-custom-${i}-${serviceId}" data-label="${label}" ${required}></textarea>`
-                    : `<input type="text" id="creavibc-custom-${i}-${serviceId}" data-label="${label}" ${required}>`;
-                html += `</label>`;
-            });
-        }
+        if (Array.isArray(form.custom)) {
+            form.custom.forEach((field, i) => {
+                //const label = field.label || `Field ${i + 1}`;
+                const label = String(field.label || sprintf( __( 'Field %d', CBS_DOMAIN ), i + 1 ));
+                const safeLabel = creavibcEscapeHtml(label);
+
+                const type = field.type || 'text';
+                const required = field.required ? 'required' : '';
+                html += `<label>${safeLabel}${required ? ' <span class="creavibc-required">*</span>' : ''}`;
+                html += type === 'textarea'
+                    ? `<textarea id="creavibc-custom-${i}-${serviceId}" data-label="${safeLabel}" ${required}></textarea>`
+                    : `<input type="text" id="creavibc-custom-${i}-${serviceId}" data-label="${safeLabel}" ${required}>`;
+                html += `</label>`;
+            });
+        }

Exploit Outline

1. Authenticate with an account having at least Author-level permissions. 2. Navigate to the Service Editor (wp-admin/post-new.php?post_type=creavibc_service). 3. Locate the 'Form Fields' settings metabox. 4. Add a new custom field and input an XSS payload into the 'Label' field (e.g., "><img src=x onerror=alert(1)>). 5. Save or publish the service. 6. Embed the booking service on a page using the provided shortcode or view the service's frontend popup. 7. When the JavaScript in booking.js renders the form, the unsanitized label will break out of the HTML attribute context or label tag and execute the payload.

Check if your site is affected.

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