Hydra Booking <= 1.2.1 - Authenticated (Custom+) Insecure Direct Object Reference to Sensitive Information Exposure via 'booking_id' Parameter
Description
The Hydra Booking – Appointment Scheduling & Booking Calendar plugin for WordPress is vulnerable to Insecure Direct Object Reference in versions up to, and including, 1.2.1 via the /wp-json/hydra-booking/v1/booking/details/{id} REST endpoint. This is due to the getBookingDetails() callback only enforcing the tfhb_manage_options capability via tfhb_manage_options_permission(), without verifying that the requested booking belongs to the currently authenticated host (the lookup in getBookingDetailsData() filters solely on the booking id supplied in the URL). This makes it possible for authenticated attackers, with Hydra Host-level access and above (a role created by the plugin which grants tfhb_manage_options), to view sensitive booking records belonging to other hosts, including attendee names, emails, phone numbers, addresses, meeting details, payment method and status, transaction history, and internal notes by iterating booking IDs.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:NTechnical Details
What Changed in the Fix
Changes introduced in v1.2.2
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-12433 (Hydra Booking IDOR) ## 1. Vulnerability Summary The **Hydra Booking** plugin for WordPress is vulnerable to an **Insecure Direct Object Reference (IDOR)** in its REST API. Specifically, the endpoint `/wp-json/hydra-booking/v1/booking/details/{id}` allow…
Show full research plan
Exploitation Research Plan: CVE-2026-12433 (Hydra Booking IDOR)
1. Vulnerability Summary
The Hydra Booking plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) in its REST API. Specifically, the endpoint /wp-json/hydra-booking/v1/booking/details/{id} allows any user with the tfhb_host role to view the booking details of any other host.
While the plugin implements a permission check (tfhb_manage_options_permission) that restricts access to "Hosts," it fails to verify if the requested {id} belongs to the authenticated user. An attacker can iterate through booking IDs to expose sensitive attendee information, including names, emails, phone numbers, and meeting locations.
2. Attack Vector Analysis
- Endpoint:
/wp-json/hydra-booking/v1/booking/details/(?P<id>[0-9]+) - HTTP Method:
GET - Authentication: Required. Any user with the
tfhb_hostrole or thetfhb_manage_optionscapability. - Vulnerable Parameter: The
idpath variable in the URL. - Preconditions: The attacker must be logged in as a "Host" (a custom role created by the plugin).
3. Code Flow
- Route Registration: In
admin/Controller/BookingController.php, the route is registered:register_rest_route( 'hydra-booking/v1', '/booking/details/(?P<id>[0-9]+)', array( 'methods' => 'GET', 'callback' => array( $this, 'getBookingDetails' ), 'permission_callback' => array(new RouteController() , 'tfhb_manage_options_permission'), ) ); - Permission Check:
RouteController::tfhb_manage_options_permissionverifies if the user has thetfhb_manage_optionscapability. This capability is granted to thetfhb_hostrole. - Vulnerable Callback: The
getBookingDetailscallback (defined inBookingController.php) is executed. According to the vulnerability description, this function (and its helpergetBookingDetailsData) retrieves booking data based purely on theidprovided in the URL without checking thehost_idagainst the current user's host ID. - Existing (Unused) Protection: Note that
BookingControllercontains a functiontfhb_verify_booking_ownership( $booking_id )(line 33) designed to check if a booking belongs to a host, but this function is not invoked in thepermission_callbackfor the details endpoint.
4. Nonce Acquisition Strategy
The WordPress REST API requires a _wpnonce for authenticated requests to prevent CSRF, usually passed in the X-WP-Nonce header.
- Role Setup: The
tfhb_hostrole is required. - Dashboard Access: Logged-in hosts access the plugin via
wp-admin/admin.php?page=hydra-booking. - Nonce Extraction:
- Navigate to
http://localhost:8080/wp-admin/admin.php?page=hydra-booking. - The plugin likely localizes its settings. Common Hydra Booking localization variables include
tfhb_admin_data. - Use
browser_evalto extract the REST nonce from the standard WordPresswpApiSettingsor the plugin-specific object. - Command:
browser_eval("window.wpApiSettings?.nonce")orbrowser_eval("window.tfhb_admin_data?.nonce").
- Navigate to
5. Exploitation Strategy
- Setup Phase:
- Create a "Victim" user with the
tfhb_hostrole. - Create an "Attacker" user with the
tfhb_hostrole. - As the Victim, create a booking. Record the ID (e.g.,
1).
- Create a "Victim" user with the
- Execution Phase:
- Authenticate as the Attacker.
- Obtain a REST nonce (see Section 4).
- Send a
GETrequest to the target endpoint targeting the Victim's booking ID.
- Request Details:
- Method:
GET - URL:
http://localhost:8080/wp-json/hydra-booking/v1/booking/details/[VICTIM_BOOKING_ID] - Headers:
X-WP-Nonce: [EXTRACTED_NONCE]Content-Type: application/json
- Method:
6. Test Data Setup
- Create Roles (if not automatic): Ensure the plugin is activated so it creates the
tfhb_hostrole. - Create Users:
wp user create victim victim@example.com --role=tfhb_host --user_pass=passwordwp user create attacker attacker@example.com --role=tfhb_host --user_pass=password
- Seed Data:
- Since the plugin uses custom tables (e.g.,
{$wpdb->prefix}tfhb_bookings), use the UI orwp db queryto insert a booking for the Victim. - A booking requires a
host_id. Ensure the Victim user has an entry in thetfhb_hoststable linked to theiruser_id. - Use the
SetupWizard::CreateHostlogic or manually link:wp db query "INSERT INTO wp_tfhb_hosts (user_id, email, status) VALUES ([VICTIM_UID], 'victim@example.com', 'activate')"wp db query "INSERT INTO wp_tfhb_bookings (host_id, status, meeting_title) VALUES ([VICTIM_HOST_ID], 'confirmed', 'Secret Strategy Meeting')"
- Since the plugin uses custom tables (e.g.,
7. Expected Results
- The server returns a
200 OKresponse. - The JSON response body contains sensitive information:
meeting_titleattendee_details(Name, Email, etc.)meeting_locations(Zoom links, addresses)internal_notes
8. Verification Steps
- Check Response: Confirm the JSON contains the string "Secret Strategy Meeting" (or whatever title was seeded).
- Verify IDOR: Ensure the Attacker's own host ID is different from the Victim's host ID by checking the database:
wp db query "SELECT id, user_id FROM wp_tfhb_hosts"
9. Alternative Approaches
If the details endpoint is patched or behaves differently, try other routes registered in BookingController::create_endpoint() that use the same permission_callback:
/booking/(?P<id>[0-9]+)(CallsgetBookingData)/booking/update-internal-note(POST request, may allow modification of other hosts' notes)/booking/cancel-booking-attendee(POST request, may allow cancelling other hosts' meetings)
All these routes use tfhb_manage_options_permission and likely suffer from the same lack of ownership validation.
Summary
The Hydra Booking plugin for WordPress contains an Insecure Direct Object Reference (IDOR) vulnerability in its REST API. Authenticated attackers with the 'tfhb_host' role can access sensitive booking details of any user by manipulating the 'id' parameter in the /wp-json/hydra-booking/v1/booking/details/{id} endpoint, as the plugin fails to verify booking ownership.
Vulnerable Code
// admin/Controller/BookingController.php:125 // Get Single Booking based on id register_rest_route( 'hydra-booking/v1', '/booking/details/(?P<id>[0-9]+)', array( 'methods' => 'GET', 'callback' => array( $this, 'getBookingDetails' ), 'permission_callback' => array(new RouteController() , 'tfhb_manage_options_permission'), ) );
Security Fix
@@ -900,6 +900,9 @@ $booking = new Booking(); if ( ! empty( $request['id'] ) ) { + if ( ! $this->tfhb_verify_booking_ownership( $request['id'] ) ) { + return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this booking.', 'hydra-booking' ), array( 'status' => 403 ) ); + } $data = array( 'id' => isset( $request['id'] ) ? $request['id'] : '', 'meeting_id' => isset( $request['meeting'] ) ? $request['meeting'] : '', @@ -998,6 +1001,11 @@ ) ); } + + if ( ! $this->tfhb_verify_booking_ownership( $booking_id ) ) { + return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this booking.', 'hydra-booking' ), array( 'status' => 403 ) ); + } + // Delete Booking $booking = new Booking(); $single_booking_meta = $booking->get( absint( $booking_id ) ); @@ -1160,6 +1168,10 @@ ); } + if ( ! $this->tfhb_verify_booking_ownership( $attendeeBooking->booking_id ) ) { + return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this booking.', 'hydra-booking' ), array( 'status' => 403 ) ); + } + if( 'confirmed' != $attendeeBooking->status ){ return rest_ensure_response( array( @@ -1217,6 +1229,11 @@ ) ); } + + if ( ! $this->tfhb_verify_booking_ownership( $attendeeBooking->booking_id ) ) { + return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this booking.', 'hydra-booking' ), array( 'status' => 403 ) ); + } + if($attendeeBooking->status == $status){ return rest_ensure_response( array( @@ -1667,6 +1684,10 @@ ); } + if ( ! $this->tfhb_verify_booking_ownership( $booking_id ) ) { + return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this booking.', 'hydra-booking' ), array( 'status' => 403 ) ); + } + $data = array( 'id' => $request['id'], 'status' => isset( $request['status'] ) ? sanitize_text_field( $request['status'] ) : '', @@ -1762,6 +1783,8 @@ $items = $request['items']; $booking_owner = !empty($request['host']) ? $request['host'] : ''; + // Only keep booking ids the current user is actually allowed to touch. + $items = ! empty( $items ) ? array_values( array_filter( $items, array( $this, 'tfhb_verify_booking_ownership' ) ) ) : $items;
Exploit Outline
1. Gain access to a WordPress account with the 'tfhb_host' role (or any role possessing the 'tfhb_manage_options' capability). 2. Obtain a valid WordPress REST API nonce (typically found in the 'wpApiSettings' or 'tfhb_admin_data' JavaScript objects on the plugin's dashboard). 3. Identify a target booking ID belonging to another host. 4. Send an authenticated GET request to `/wp-json/hydra-booking/v1/booking/details/{id}`, replacing `{id}` with the target booking ID and including the nonce in the 'X-WP-Nonce' header. 5. The server will respond with the full details of the booking, including attendee PII (name, email, phone) and internal host notes.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.