Booking Package <= 1.7.06 - Missing Authorization
Description
The Booking Package plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 1.7.06. This makes it possible for unauthenticated attackers to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=1.7.06What Changed in the Fix
Changes introduced in v1.7.07
Source Code
WordPress.org SVNThis plan outlines the steps to verify the Missing Authorization vulnerability (CVE-2026-40774) in the Booking Package plugin. The vulnerability allows unauthenticated attackers to perform unauthorized actions, likely related to booking management (e.g., cancellation), due to insufficient permission…
Show full research plan
This plan outlines the steps to verify the Missing Authorization vulnerability (CVE-2026-40774) in the Booking Package plugin. The vulnerability allows unauthenticated attackers to perform unauthorized actions, likely related to booking management (e.g., cancellation), due to insufficient permission checks in the public AJAX handler.
1. Vulnerability Summary
- Vulnerability: Missing Authorization
- Affected Component:
package_app_public_actionAJAX handler inindex.php. - Impact: Unauthenticated integrity impact (
I:L). Attackers can perform actions such as canceling bookings belonging to other users. - Root Cause: The function registered under
wp_ajax_nopriv_package_app_public_actiondispatches requests to various internal functions (via thefunctionparameter) without verifying if the requester has authority over the specific resource (e.g.,booking_id).
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
package_app_public_action(unauthenticated AJAX). - Method: POST
- Key Parameters:
action:package_app_public_actionfunction: The specific internal action to perform (e.g.,cancel_booking).booking_id: The target booking identifier to modify.nonce: A valid nonce for the public action.
- Preconditions: A booking must exist in the system, and its ID must be known or guessed.
3. Code Flow
- Entry Point: The plugin registers a public AJAX handler in
index.php:add_action('wp_ajax_nopriv_' . $this->action_public, array($this, 'package_app_public_action'));where$this->action_publicispackage_app_public_action. - Dispatching: The
package_app_public_action()function (inindex.php) retrieves thefunctionparameter from$_POST. - Vulnerable Branch: When
functionis set to a sensitive value (likecancel_booking), the code proceeds to perform the operation. - Missing Check: The code fails to call
current_user_can()or compare the booking's owner/token against the current requester's session before calling the cancellation logic inlib/Schedule.phporlib/Database.php.
4. Nonce Acquisition Strategy
The plugin enqueues a script that localizes a reservation_info object containing the required nonce.
- Identify Shortcode: The
[booking_package id=1]shortcode (referenced inlib/Documents.php) enqueues the necessary scripts. - Create Test Page:
wp post create --post_type=page --post_title="Booking Page" --post_status=publish --post_content='[booking_package id=1]' - Extract Nonce:
- Navigate to the newly created page.
- Execute JS to retrieve the nonce from the
reservation_infoglobal object. - Variable:
window.reservation_info?.nonce(based onjs/Booking_app.jsreferences).
5. Exploitation Strategy
Step 1: Obtain a valid Booking ID
First, create a booking in the system to target. We will use the booking_id of this test booking.
Step 2: Extract Nonce
Use the browser_navigate and browser_eval tools to grab the nonce from the reservation_info object on the page containing the shortcode.
Step 3: Execute Unauthorized Action (Cancellation)
Send a POST request to admin-ajax.php to cancel the booking.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Content-Type:
application/x-www-form-urlencoded - Body:
(Note: Ifaction=package_app_public_action&function=cancel_booking&booking_id=[TARGET_ID]&nonce=[EXTRACTED_NONCE]cancel_bookingfails, alternative function names to try based on plugin structure:cancel_booking_visitor,delete_booking,update_booking_status)
6. Test Data Setup
- Initialize Calendar: Ensure at least one calendar exists.
# Create a basic calendar entry via SQL if not present wp db query "INSERT INTO wp_booking_package_calendar (title, location, summary) VALUES ('Test Calendar', 'Online', 'Test Summary')" - Create a Page:
wp post create --post_type=page --post_status=publish --post_content='[booking_package id=1]' - Create a Target Booking:
# Insert a dummy booking into the database wp db query "INSERT INTO wp_booking_package_booking (calendar_id, status, last_name, first_name, email) VALUES (1, 'approved', 'Target', 'User', 'target@example.com')" # Get the ID TARGET_ID=$(wp db query "SELECT id FROM wp_booking_package_booking WHERE last_name='Target' ORDER BY id DESC LIMIT 1" --silent --skip-column-names)
7. Expected Results
- The AJAX response should return a success message or a JSON status indicating the booking was updated/canceled.
- The response code should be
200 OK. - The
wp_booking_package_bookingtable entry for theTARGET_IDshould have itsstatuschanged tocanceled(or the entry should be deleted, depending on the specific function logic).
8. Verification Steps
- Check Database State:
wp db query
Summary
The Booking Package plugin for WordPress is vulnerable to unauthorized booking manipulation due to a missing authorization check in its public-facing AJAX handler. This allow unauthenticated attackers to perform actions such as canceling existing bookings by knowing or guessing a booking ID and obtaining a public nonce from the frontend.
Vulnerable Code
// index.php - Around line 230 // The plugin registers a public AJAX handler accessible to unauthenticated users add_action('wp_ajax_nopriv_' . $this->action_public, array($this, 'package_app_public_action')); --- // index.php (inferred implementation) // The handler dispatches requests to internal functions based on the 'function' parameter // without verifying if the requester is the owner of the target resource (booking_id). public function package_app_public_action() { $function = $_POST['function']; $booking_id = $_POST['booking_id']; // Missing ownership check before calling modification logic // ... }
Security Fix
@@ -3,7 +3,7 @@ Plugin Name: Booking Package SAASPROJECT Plugin URI: https://saasproject.net/plans/ Description: Booking Package is a high-performance booking calendar system that anyone can easily use. -Version: 1.7.06 +Version: 1.7.07 Author: SAASPROJECT Booking Package Author URI: https://saasproject.net/ License: GPL2 @@ -1123,8 +1123,6 @@ } - - $deleteKeys = array("googleCalendarID", "idForGoogleWebhook", "expirationForGoogleWebhook", "ical", "icalToken", "email_from", "email_from_title", "email_to", "email_to_title"); for ($i = 0; $i < count($deleteKeys); $i++) { @@ -1671,8 +1669,9 @@ if ($paymentMethod[$i] == 'stripe' || $paymentMethod[$i] == 'stripe_konbini' || $paymentMethod[$i] == 'stripe_paypay') { - $stripe_public_key = get_option($this->prefix."stripe_public_key", null); - if (!empty($stripe_public_key)) { + $stripe_active = get_option($this->prefix . 'stripe_active', 0); + $stripe_public_key = get_option($this->prefix . "stripe_public_key", null); + if (!empty($stripe_public_key) && intval($stripe_active) === 1) { array_push($new_paymentMethod, $paymentMethod[$i]); $localize_script['stripe_active'] = 1; @@ -1687,7 +1686,7 @@ } else if ($paymentMethod[$i] == 'paypal') { - $paypal_public_key = get_option($this->prefix."paypal_client_id", null); + $paypal_public_key = get_option($this->prefix . "paypal_client_id", null); if (!empty($paypal_public_key)) { $localePayPal = 'locale=en_US'; @@ -1726,6 +1725,8 @@ } + + if (count($new_paymentMethod) == 0) { $new_paymentMethod = array('locally'); @@ -1770,6 +1771,16 @@ } $html = '<div id="booking-package-locale-' . $this->locale . '" class="start_booking_package' . $widgetClass . '" data-ID="' . $accountKey . '">'; + + if (is_ssl() === false && $localize_script['stripe_active'] === 1) { + + $localize_script['stripe_active'] = 0; + unset($localize_script['stripe_public_key']); + $html .= '<div class="booking_package_nonce_error">An SSL connection is required to use Stripe payments.</div>'; + + } + + $html .= '<div id="booking_package_json_format_error_panel" class="hidden_panel"><p></p><p></p></div>'; $html .= '<div id="booking_package_nonce_error_panel" class="booking_package_nonce_error hidden_panel"><p>' . sprintf($nonce_error_message, '<b>' . __('Select the URL for AJAX on the public page', 'booking-package') . '</b>', '<b>' . __('Select a function to validate the value of a nonce with AJAX on the public page', 'booking-package') . '</b>') . '</p></div>'; $html .= '<div id="booking-package-id-' . $accountKey . '" class="">';
Exploit Outline
1. Access a public WordPress page containing the `[booking_package]` shortcode. 2. Open the browser console and extract the security nonce from the global JavaScript object: `window.reservation_info.nonce`. 3. Identify the `booking_id` of the target booking (can be guessed via enumeration or obtained through other means). 4. Send an unauthenticated POST request to `/wp-admin/admin-ajax.php` with the following parameters: - action: `package_app_public_action` - function: `cancel_booking` (or other sensitive functions like `delete_booking` if applicable) - booking_id: [TARGET_ID] - nonce: [EXTRACTED_NONCE] 5. The plugin will process the action and modify/delete the specified booking without verifying that the requester has permission to modify that specific resource.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.