[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$ffhTIj91goICqUWJvoy9_JT73U5g5RLPOKL7WKCo5cWA":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":25,"research_verified":26,"research_rounds_completed":27,"research_plan":28,"research_summary":29,"research_vulnerable_code":30,"research_fix_diff":31,"research_exploit_outline":32,"research_model_used":33,"research_started_at":34,"research_completed_at":35,"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":26,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":26,"source_links":36},"CVE-2026-2519","online-scheduling-and-appointment-booking-system-bookly-unauthenticated-price-manipulation-via-tips","Online Scheduling and Appointment Booking System – Bookly \u003C= 27.0 - Unauthenticated Price Manipulation via 'tips'","The Online Scheduling and Appointment Booking System – Bookly plugin for WordPress is vulnerable to price manipulation via the 'tips' parameter in all versions up to, and including, 27.0. This is due to the plugin trusting a user-supplied input without server-side validation against the configured price. This makes it possible for unauthenticated attackers to submit a negative number to the 'tips' parameter, causing the total price to be reduced to zero.","bookly-responsive-appointment-booking-tool",null,"\u003C=27.0","27.1","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","External Control of Assumed-Immutable Web Parameter","2026-04-08 00:00:00","2026-04-09 15:22:31",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fead87d8b-2659-4e8b-a0b9-138b1db89e36?source=api-prod",2,[22,23,24],"lib\u002FUserBookingData.php","main.php","readme.txt","researched",false,3,"# Exploitation Research Plan: CVE-2026-2519 - Bookly Price Manipulation\n\n## 1. Vulnerability Summary\nThe **Online Scheduling and Appointment Booking System – Bookly** plugin (versions \u003C= 27.0) contains a logic flaw where user-supplied input for the `tips` parameter is trusted without server-side validation. Specifically, the plugin fails to ensure that the tip amount is a non-negative value. An unauthenticated attacker can submit a negative value for the `tips` parameter during the booking process, which is then added to the total service price, effectively reducing the final amount to zero and allowing free bookings of paid services.\n\n## 2. Attack Vector Analysis\n*   **Endpoint:** `wp-admin\u002Fadmin-ajax.php`\n*   **Action:** `bookly_render_payment` (or `bookly_checkout`)\n*   **Vulnerable Parameter:** `tips`\n*   **Authentication:** Unauthenticated (accessible via `wp_ajax_nopriv_` handlers)\n*   **Preconditions:** \n    1. Bookly plugin must be active.\n    2. A service with a non-zero price must be configured.\n    3. A page must contain the Bookly booking form shortcode: `[bookly-form]`.\n\n## 3. Code Flow\n1.  The booking process is managed by `Bookly\\Lib\\UserBookingData`.\n2.  During the \"Payment\" step of the booking wizard, the frontend sends an AJAX request to update the user's booking session.\n3.  The property `protected $tips;` in `lib\u002FUserBookingData.php` (line 104) is populated from the request.\n4.  The `$properties` array (line 144) includes `'tips'`, indicating it is a dynamically settable property from user input.\n5.  In the calculation phase (likely in `Bookly\\Lib\\Cart::getInfo()`), the plugin sums the service price and the `tips` value.\n6.  Because the code lacks a check like `max( 0, $tips )`, a negative value (e.g., `-50.00`) is subtracted from the total.\n\n## 4. Nonce Acquisition Strategy\nBookly uses a CSRF token (nonce) for all its AJAX operations. This token is typically localized for the frontend.\n\n1.  **Identify Shortcode:** The plugin uses `[bookly-form]`.\n2.  **Create Test Page:**\n    `wp post create --post_type=page --post_title=\"Booking\" --post_status=publish --post_content='[bookly-form]'`\n3.  **Navigate and Extract:**\n    Navigate to the new page.\n    The nonce is stored in a JavaScript object localized by the plugin, usually `bookly_ajax` or `BooklyL10n`.\n4.  **Browser Eval:**\n    Use `browser_eval(\"bookly_options.csrf_token\")` to retrieve the token.\n\n## 5. Exploitation Strategy\nThe goal is to proceed through the booking steps and inject a negative tip that zeroes out the total price.\n\n### Step 1: Initialize Session and Progress Steps\nBookly uses a multi-step AJAX process. You must simulate moving through the steps.\n*   **Action 1 (Service Selection):** `action=bookly_render_time`\n*   **Action 2 (Time Selection):** `action=bookly_render_details`\n*   **Action 3 (User Details):** `action=bookly_render_payment`\n\n### Step 2: Inject Negative Tip\nWhen calling the step that calculates or renders the payment, or during the final checkout, include the negative `tips` parameter.\n\n**HTTP Request (Example):**\n```http\nPOST \u002Fwp-admin\u002Fadmin-ajax.php HTTP\u002F1.1\nContent-Type: application\u002Fx-www-form-urlencoded\n\naction=bookly_render_payment&\ncsrf_token=[NONCE]&\nform_id=[FORM_ID]&\ntips=-100.00&\n...[other session parameters]...\n```\n\n### Step 3: Finalize Checkout\nIf the total price is reduced to zero, the plugin may allow the \"Local Payment\" (or skip payment) to succeed even for premium services.\n\n**HTTP Request (Checkout):**\n```http\nPOST \u002Fwp-admin\u002Fadmin-ajax.php HTTP\u002F1.1\nContent-Type: application\u002Fx-www-form-urlencoded\n\naction=bookly_checkout&\ncsrf_token=[NONCE]&\nform_id=[FORM_ID]&\ntips=-100.00\n```\n\n## 6. Test Data Setup\n1.  **Staff Member:** Create a staff member.\n2.  **Service:** Create a service named \"Premium Consulting\" with a price of `100.00`.\n3.  **Page:** Create a page with `[bookly-form]`.\n4.  **Settings:** Ensure at least one payment method is active (e.g., \"Local Payment\").\n\n## 7. Expected Results\n*   The `bookly_render_payment` response should return HTML\u002FJSON showing a \"Total\" of `0.00`.\n*   The `bookly_checkout` response should indicate success (`\"success\": true`) and create an appointment without requiring a valid payment gateway transaction.\n\n## 8. Verification Steps\n1.  **Check Database:** Use `wp db query \"SELECT * FROM wp_bookly_payments ORDER BY id DESC LIMIT 1;\"`.\n2.  **Verify Total:** Confirm the `total` column in the database for the new payment record is `0.00` despite the service price being `100.00`.\n3.  **Check Tip Value:** Confirm the `tips` column contains the negative value (e.g., `-100.00`).\n\n## 9. Alternative Approaches\nIf `bookly_render_payment` does not allow the manipulation, attempt the injection at the `bookly_save_appointment_details` action or directly during the `bookly_checkout` action.\n\nIf the form uses a different JS variable, use `browser_eval(\"Object.keys(window).filter(k => k.includes('bookly'))\")` to find the correct localization object containing the `csrf_token`.","The Bookly plugin for WordPress is vulnerable to price manipulation in versions up to and including 27.0 because it trusts user-supplied input for the 'tips' parameter without server-side validation. An unauthenticated attacker can submit a negative value for this parameter during the checkout process, which is subtracted from the total service cost, allowing them to book appointments for free.","\u002F\u002F lib\u002FUserBookingData.php:104\n    \u002F** @var float *\u002F\n    protected $tips;\n\n---\n\n\u002F\u002F lib\u002FUserBookingData.php:144\n        \u002F\u002F Step payment\n        'coupon_code',\n        'gift_code',\n        'tips',\n        'deposit_full',\n\n---\n\n\u002F\u002F lib\u002FUserBookingData.php:361\n    public function fillData( array $data )\n    {\n        foreach ( $data as $name => $value ) {\n            if ( in_array( $name, $this->properties ) ) {\n                $this->{$name} = $value;\n            } elseif ( $name == 'chain' ) {\n\n---\n\n\u002F\u002F lib\u002FUserBookingData.php (inferred from patch diff around line 1898)\n    public function setTips( $tips )\n    {\n        $this->tips = $tips;\n\n        return $this;\n    }","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fbookly-responsive-appointment-booking-tool\u002F27.0\u002Flib\u002FUserBookingData.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fbookly-responsive-appointment-booking-tool\u002F27.1\u002Flib\u002FUserBookingData.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fbookly-responsive-appointment-booking-tool\u002F27.0\u002Flib\u002FUserBookingData.php\t2026-03-06 09:39:24.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fbookly-responsive-appointment-booking-tool\u002F27.1\u002Flib\u002FUserBookingData.php\t2026-03-12 08:49:26.000000000 +0000\n@@ -144,7 +144,6 @@\n         \u002F\u002F Step payment\n         'coupon_code',\n         'gift_code',\n-        'tips',\n         'deposit_full',\n         \u002F\u002F Cart item keys being edited\n         'edit_cart_keys',\n@@ -361,7 +360,9 @@\n     public function fillData( array $data )\n     {\n         foreach ( $data as $name => $value ) {\n-            if ( in_array( $name, $this->properties ) ) {\n+            if ( $name === 'tips' ) {\n+                $this->setTips( $value );\n+            } elseif ( in_array( $name, $this->properties ) ) {\n                 $this->{$name} = $value;\n             } elseif ( $name == 'chain' ) {\n                 $chain_items = $this->chain->getItems();\n@@ -1898,7 +1899,7 @@\n      *\u002F\n     public function setTips( $tips )\n     {\n-        $this->tips = $tips;\n+        $this->tips = max( 0, (float) $tips );\n \n         return $this;\n     }","The exploit targets the AJAX booking flow of the Bookly plugin. An unauthenticated attacker first obtains a valid CSRF token (`csrf_token`) from the localized JavaScript on any page containing the `[bookly-form]` shortcode. The attacker then initiates a multi-step booking process via `admin-ajax.php`. During the step that renders payment information or finalizes the checkout (actions like `bookly_render_payment` or `bookly_checkout`), the attacker includes the `tips` parameter in the POST request with a negative value (e.g., `tips=-50.00`). Because the server lacks validation on this input and uses a setter that directly assigns the value, the negative 'tip' is added to the total, effectively subtracting from the service price. If the total is reduced to zero, the plugin allows the appointment to be finalized without a valid payment transaction.","gemini-3-flash-preview","2026-04-17 20:21:28","2026-04-17 20:21:48",{"type":37,"vulnerable_version":38,"fixed_version":11,"vulnerable_browse":39,"vulnerable_zip":40,"fixed_browse":41,"fixed_zip":42,"all_tags":43},"plugin","27.0","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fbookly-responsive-appointment-booking-tool\u002Ftags\u002F27.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fbookly-responsive-appointment-booking-tool.27.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fbookly-responsive-appointment-booking-tool\u002Ftags\u002F27.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fbookly-responsive-appointment-booking-tool.27.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fbookly-responsive-appointment-booking-tool\u002Ftags"]