CVE-2026-40774

Booking Package <= 1.7.06 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
1.7.07
Patched in
10d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.7.06
PublishedApril 21, 2026
Last updatedApril 30, 2026
Affected pluginbooking-package

What Changed in the Fix

Changes introduced in v1.7.07

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

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…

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_action AJAX handler in index.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_action dispatches requests to various internal functions (via the function parameter) 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_action
    • function: 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

  1. 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_public is package_app_public_action.
  2. Dispatching: The package_app_public_action() function (in index.php) retrieves the function parameter from $_POST.
  3. Vulnerable Branch: When function is set to a sensitive value (like cancel_booking), the code proceeds to perform the operation.
  4. 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 in lib/Schedule.php or lib/Database.php.

4. Nonce Acquisition Strategy

The plugin enqueues a script that localizes a reservation_info object containing the required nonce.

  1. Identify Shortcode: The [booking_package id=1] shortcode (referenced in lib/Documents.php) enqueues the necessary scripts.
  2. Create Test Page:
    wp post create --post_type=page --post_title="Booking Page" --post_status=publish --post_content='[booking_package id=1]'
    
  3. Extract Nonce:
    • Navigate to the newly created page.
    • Execute JS to retrieve the nonce from the reservation_info global object.
    • Variable: window.reservation_info?.nonce (based on js/Booking_app.js references).

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:
    action=package_app_public_action&function=cancel_booking&booking_id=[TARGET_ID]&nonce=[EXTRACTED_NONCE]
    
    (Note: If cancel_booking fails, alternative function names to try based on plugin structure: cancel_booking_visitor, delete_booking, update_booking_status)

6. Test Data Setup

  1. 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')"
    
  2. Create a Page:
    wp post create --post_type=page --post_status=publish --post_content='[booking_package id=1]'
    
  3. 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_booking table entry for the TARGET_ID should have its status changed to canceled (or the entry should be deleted, depending on the specific function logic).

8. Verification Steps

  1. Check Database State:
    wp db query
    
Research Findings
Static analysis — not yet PoC-verified

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

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/booking-package/1.7.06/index.php /home/deploy/wp-safety.org/data/plugin-versions/booking-package/1.7.07/index.php
--- /home/deploy/wp-safety.org/data/plugin-versions/booking-package/1.7.06/index.php	2026-03-23 03:25:52.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/booking-package/1.7.07/index.php	2026-04-15 03:52:14.000000000 +0000
@@ -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.