Amelia <= 2.1.3 - Insecure Direct Object Reference to Authenticated (Employee+) Privilege Escalation via 'externalId' Parameter
Description
The Booking for Appointments and Events Calendar – Amelia plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 2.1.3. This is due to the `UpdateProviderCommandHandler` failing to validate changes to the `externalId` field when a Provider (Employee) user updates their own profile. The `externalId` maps directly to a WordPress user ID and is passed to `wp_set_password()` and `wp_update_user()` without authorization checks. This makes it possible for authenticated attackers, with Provider-level (Employee) access and above, to take over any WordPress account — including Administrator — by injecting an arbitrary `externalId` value when updating their own provider profile.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HTechnical Details
What Changed in the Fix
Changes introduced in v2.2
Source Code
WordPress.org SVN# Research Plan: CVE-2026-5465 Amelia Privilege Escalation ## 1. Vulnerability Summary The Amelia plugin (<= 2.1.3) contains an Insecure Direct Object Reference (IDOR) vulnerability in the `UpdateProviderCommandHandler`. When an authenticated user with "Provider" (Amelia Employee) privileges update…
Show full research plan
Research Plan: CVE-2026-5465 Amelia Privilege Escalation
1. Vulnerability Summary
The Amelia plugin (<= 2.1.3) contains an Insecure Direct Object Reference (IDOR) vulnerability in the UpdateProviderCommandHandler. When an authenticated user with "Provider" (Amelia Employee) privileges updates their own profile, the application fails to validate the externalId parameter. In Amelia's architecture, externalId maps directly to a WordPress User ID. Because this value is passed to sensitive WordPress functions like wp_update_user() and wp_set_password() without verifying that the externalId matches the authenticated user's own ID, a Provider can modify the credentials of any WordPress user, including Administrators.
2. Attack Vector Analysis
- Endpoint:
admin-ajax.php?action=wpamelia_api&call=/users/providers/{id} - Method:
POST(Amelia usesPOSTfor updates, often mimicking RESTfulPUTbehavior via route handling). - Vulnerable Parameter:
externalId(within the JSON request body). - Authentication Required: Authenticated user with the
Amelia Employee(Provider) role. - Preconditions: The attacker must know the WordPress User ID of the target (typically
1for the primary administrator).
3. Code Flow
- Entry Point:
ameliabooking.phpregisters the AJAX actionwpamelia_apiviaPlugin::wpAmeliaApiCall(). - Routing:
Routes::routes($app, $container)defines the Slim framework routes. The route/users/providers/{id}is mapped to theUpdateProviderCommandHandler. - Command Handling:
UpdateProviderCommandHandlerreceives the request. It extracts data from the JSON body, including theexternalId. - Authorization Failure: The handler checks if the authenticated user is a Provider and is updating their own Amelia ID (
{id}). However, it does not check if theexternalId(the WP User ID) belongs to that same user. - Sink: The command handler or the underlying
UserService/UserRepositorycallswp_update_user()orwp_set_password()using the attacker-suppliedexternalId.
4. Nonce Acquisition Strategy
Amelia requires a WordPress nonce for all wpamelia_api AJAX calls. For authenticated backend users (Employees/Admins), the nonce is generated and localized for the Vue.js dashboard.
- Login: Authenticate as the Provider (Employee) user.
- Identify Page: The Amelia backend dashboard is usually located at
/wp-admin/admin.php?page=wpamelia-dashboardor/wp-admin/admin.php?page=wpamelia-employees. - Extraction:
- Amelia typically localizes its configuration in the
wpAmeliaNonceglobal variable or within a large configuration object. - Action: Navigate to the Amelia Employees page in the WordPress backend.
- JS Command:
browser_eval("window.wpAmeliaNonce")orbrowser_eval("ameliaBookingData.nonce"). - Note: In version 2.1.3, the common variable is
window.wpAmeliaNonce.
- Amelia typically localizes its configuration in the
5. Exploitation Strategy
Step 1: Data Setup
- Create a WordPress user with the
Amelia Employeerole. - Ensure the Amelia plugin is configured so this user is recognized as a "Provider" within the Amelia > Employees section.
- Identify the target Admin's WP User ID (default:
1).
Step 2: Obtain Nonce and Provider ID
- Log in as the Employee.
- Navigate to the WordPress Admin area.
- Retrieve the
wpAmeliaNonce. - Retrieve the Employee's Amelia Provider ID (can be seen in the URL when editing the profile or via a
GETrequest to/users/providers).
Step 3: Execute Hijack Request
Send a POST request to update the profile, injecting the Administrator's WP ID into externalId.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php?action=wpamelia_api&call=/users/providers/{PROVIDER_ID} - Headers:
Content-Type: application/jsonX-Amelia-Nonce: {NONCE_VALUE}(Amelia sometimes expects the nonce in a custom header or as awpAmeliaNonceparameter in the body/URL).
- Payload (JSON):
{
"id": {PROVIDER_ID},
"firstName": "Attacker",
"lastName": "User",
"email": "attacker@example.com",
"externalId": 1,
"password": "pwned_password123",
"status": "visible"
}
Step 4: Access Admin Account
- Log out.
- Log in to
/wp-login.phpas the Administrator (e.g., usernameadmin) using the passwordpwned_password123.
6. Test Data Setup
- Target User: User ID
1(Admin). - Attacker User: Create WP user
employee_atk, rolesubscriber(Amelia will elevate this or link it). - Amelia Config:
- Go to Amelia > Employees.
- Add
employee_atkas an Employee. - Record the internal Amelia ID assigned to them (e.g.,
5).
7. Expected Results
- The API should return a
200 OKor201 Createdresponse with a JSON body confirming the update. - The WordPress database (
wp_userstable) will reflect the change for User ID1. - The Administrator will no longer be able to log in with their original password.
8. Verification Steps
- Check via WP-CLI:
# Check if the admin user's email was changed wp user get 1 --fields=user_email # Check if the password was updated (attempt login) wp user check-password admin pwned_password123 - Verify Amelia State:
# Check if the provider is now linked to the admin ID in Amelia tables wp db query "SELECT * FROM wp_amelia_users WHERE id = {PROVIDER_ID}"
9. Alternative Approaches
- Email Hijack: Instead of changing the password, change the
emailfield associated withexternalId: 1. Then use the WordPress "Lost your password?" feature to reset the Admin password via the attacker's email. - Method Spoofing: If the endpoint expects
PUT, Amelia's Slim implementation might require anX-HTTP-Method-Override: PUTheader or a_method: PUTparameter in the JSON body. - Generic Nonce: Check if the nonce used for public bookings (often found on the frontend page with the
[ameliabooking]shortcode) is accepted by the backend API. (Though the vulnerability specifies Employee-level access).
Summary
The Amelia plugin for WordPress is vulnerable to privilege escalation via an Insecure Direct Object Reference (IDOR) in the UpdateProviderCommandHandler. Authenticated users with Provider (Employee) permissions can update their own profile while supplying an arbitrary 'externalId' value, which corresponds to a WordPress User ID. Because the plugin uses this ID to call sensitive functions like wp_set_password() without verifying ownership, an attacker can take over any WordPress account, including Administrators.
Vulnerable Code
// ameliabooking.php line 185 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.1.3 +Version: 2.2 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.1.3'); + define('AMELIA_VERSION', '2.2'); } // Const for site URL
Exploit Outline
1. Authenticate to the WordPress site as a user with the 'Amelia Employee' (Provider) role. 2. Navigate to the Amelia dashboard and extract the AJAX nonce from the 'window.wpAmeliaNonce' JavaScript variable. 3. Identify the target WordPress Administrator's User ID (usually 1). 4. Send a POST request to '/wp-admin/admin-ajax.php?action=wpamelia_api&call=/users/providers/{provider_id}', where {provider_id} is the attacker's own Amelia ID. 5. Include a JSON payload containing the keys 'externalId' (set to the Administrator's WP User ID) and 'password' (set to a new password of the attacker's choosing). 6. The plugin will execute wp_set_password() for the target ID, allowing the attacker to log in as the Administrator via wp-login.php.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.