Amelia Booking Pro ≤ 9.1.2: Authenticated Customer-to-Admin Password Reset via IDOR
CVE-2026-2931
Summary
An authenticated privilege escalation vulnerability was discovered in Amelia Booking Pro ≤ 9.1.2 (CVE-2026-2931) that allows a low-privileged Amelia customer to reset the password of arbitrary WordPress users, including administrators, under common configurations.
Amelia Booking Pro is a widely used WordPress plugin designed to automate appointment scheduling and event booking for service-based businesses. It is used by salons, healthcare providers, consultants, fitness studios, and other appointment-driven organizations to manage bookings, payments, and customer communications directly from their WordPress sites. The free version of the plugin has over 50,000 active installations on WordPress.org, and the premium version is sold through both the developer’s website wpamelia.com and select third-party marketplaces. For more information about the plugin, see the WordPress.org plugin page. Given the plugin’s broad adoption across businesses that handle customer data and payments, the security implications of this vulnerability are significant.
Why This Matters
In affected setups, a regular Amelia customer can move from “customer portal access” to full WordPress account takeover by abusing profile update logic. If the attacker targets a WordPress admin account, this becomes a full site compromise path.
A WordPress administrator account has near-total control over the site. Administrators can install and modify plugins and themes, edit PHP files directly through the built-in code editor, create and delete other user accounts, and access the site’s database through plugin interfaces. In the hands of an attacker, this level of access opens the door to a wide range of malicious outcomes.
For example, an attacker who gains administrator privileges could inject malicious JavaScript into the site's pages to redirect visitors to phishing or malware distribution sites (Sucuri, 2022; Trend Micro). They could install backdoor plugins that persist even after the initial compromise is cleaned up, giving them long-term stealth access (Sucuri, 2025; TechRadar, 2025). In real-world WordPress compromises, attackers have been observed using hijacked admin accounts to deploy SEO spam that injects thousands of rogue URLs to manipulate search rankings (Sucuri, 2025; CyberPress, 2025), plant cryptocurrency miners that run in visitors' browsers (Wordfence, 2017; The Hacker News, 2018), or exfiltrate sensitive customer data including payment information from e-commerce integrations (Zscaler). In the most severe cases, if the WordPress installation runs with elevated system privileges, an attacker could potentially leverage admin access to achieve Remote Code Execution (RCE) on the underlying server, pivoting from a web application compromise to full infrastructure access.
How Amelia Links Customers to WordPress Users
Amelia maintains its own internal user table, separate from the WordPress user system. To bridge the two systems, each Amelia customer record contains a field called **externalId**, which stores the corresponding WordPress user ID. This mapping is what allows Amelia to synchronize profile changes—including password updates—between its own system and WordPress.
The table below illustrates this relationship:
This mapping is security-sensitive because it determines which WordPress account is affected whenever Amelia synchronizes profile data. When a customer updates their profile through the Amelia customer portal, the application reads the **externalId** from their record and uses it to apply changes—including password changes—to the corresponding WordPress account via the **wp_set_password()** function.
How the Profile Update Should Work
According to the principle of least privilege and secure API design, a customer self-service endpoint should only permit changes to fields the customer owns. Identity-linking fields like **externalId** should be treated as immutable server-side state: set once during account creation and never modifiable through client-facing API requests. The OWASP Mass Assignment Cheat Sheet specifically warns against this pattern, recommending that developers use allowlists to restrict which fields can be bound from user input and use Data Transfer Objects (DTOs) to avoid binding input directly to internal data models.
What Actually Happens
Instead of enforcing these boundaries, the Amelia customer profile update API accepts security-sensitive fields directly from client input, including both **externalId** and **password**. The authorization logic checks that the JWT token belongs to the Amelia customer record being edited, but it does not enforce that **externalId** remains bound to the customer’s own WordPress account. The update flow merges trusted stored user data with untrusted request data, and because no field-level filtering is applied, the attacker-supplied **externalId** survives into the final user object. The password-update branch then uses that attacker-controlled **externalId** as the WordPress user ID in a **wp_set_password()** call. The data layer also persists the modified **externalId**, so the remapping is not just transient—it permanently links the attacker’s Amelia account to the victim’s WordPress account.
In short: customer-controlled identifier remapping combined with password synchronization to the linked WordPress account results in an arbitrary WordPress password reset (IDOR / privilege escalation).
Vulnerability Flow
The exploit chain is straightforward. First, the attacker authenticates as a normal Amelia customer and receives a valid cabinet JWT. Next, the attacker calls the customer update endpoint for their own Amelia customer ID. In the update payload, the attacker sets **externalId** to the target WordPress user ID (for example, admin user ID 1) and **password** to an attacker-chosen value. The server-side logic accepts the update without validating whether the caller has any relationship to the target WordPress user, and calls **wp_set_password()** using the supplied **externalId**. At that point, the target WordPress account’s password has been changed to the attacker’s chosen value, and the attacker can log in as that user.
Root Cause
This is a classic object-level authorization flaw combined with mass-assignment behavior on a sensitive field. The endpoint allows customer-originated updates containing **externalId** and **password**. The authorization checks confirm the caller is allowed to update their own Amelia record, but they do not prevent remapping to a different WordPress user ID. The code then trusts the remapped **externalId** when invoking **wp_set_password()**.
The issue maps well to two established weakness classifications. The first is CWE-639: Authorization Bypass Through User-Controlled Key, commonly referred to as Insecure Direct Object Reference (IDOR). According to MITRE, this weakness occurs when an application’s authorization functionality does not prevent one user from gaining access to another user’s data or record by modifying the key value identifying the data. The second is CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes also known as the mass assignment pattern. MITRE describes this as a weakness where the product receives input that specifies multiple attributes to be updated in an object but does not properly control which attributes can be modified. The OWASP API Security Top 10 (2019 edition) also highlights mass assignment as a standalone risk category, noting that API endpoints become vulnerable when they automatically convert client parameters into internal object properties without considering sensitivity.
Impact
Successful exploitation of this vulnerability allows an attacker to take over any WordPress user account on the affected site, including administrator accounts. Because WordPress administrators have full control over the site—including the ability to install plugins, edit theme files, and execute arbitrary PHP code—this vulnerability represents a direct path from low-privileged customer access to full site compromise.
In practice, an attacker who gains administrator access can perform any number of damaging actions. They can install malicious plugins or modify existing theme files to inject backdoors that survive password resets and plugin updates. They can redirect site visitors to phishing pages or malware distribution sites. They can exfiltrate customer data, including names, email addresses, booking history, and potentially payment information if the site integrates with payment gateways through WooCommerce or similar systems. If the WordPress installation has elevated filesystem or database privileges, the attacker may be able to achieve Remote Code Execution (RCE) on the underlying server, escalating from a web application compromise to full infrastructure access.
The broader WordPress ecosystem has seen numerous real-world attacks that follow this pattern. Compromised admin accounts have been used to deploy SEO spam across thousands of pages, install cryptocurrency mining scripts, and establish persistent backdoors that survive multiple cleanup attempts. Given that Amelia is used by businesses that handle customer appointments, personal data, and often financial transactions, the potential for harm is substantial.
Final Note
This vulnerability is a strong example of why identity-link fields (**externalId**, **user_id**, **owner_id**) must be treated as privileged server-side state, never as mutable client input. A small trust boundary mistake turned a normal customer feature into a full privilege escalation path.
Timeline
Discovered Vulnerability - 02-20-2026
Initial Disclosure to Vendor - 02-20-2026
Response from Wordfence - 02-21-2026
Vendor Released Patch N/A
Public Disclosure Date - 03-21-2026



