CVE-2026-9188

Appointment Bookings for Zoom GoogleMeet and more – Wappointment <= 2.7.6 - Unauthenticated Insecure Direct Object Reference via Predictable 'edit_key' / 'appointmentkey' Parameter

mediumAuthorization Bypass Through User-Controlled Key
5.3
CVSS Score
5.3
CVSS Score
medium
Severity
2.7.7
Patched in
1d
Time to patch

Description

The Appointment Bookings for Zoom GoogleMeet and more – Wappointment plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to and including 2.7.6 via the `appointmentkey` parameter due to the appointment `edit_key` — the sole authorization token consumed by `tryCancel()` — being generated as a predictable, unsalted MD5 hash of only `client_id` (a sequential integer), `start_at` (a publicly observable appointment timestamp), and `staff_id` (a small enumerable integer), with no secret salt or random component, and the unauthenticated cancellation and rescheduling REST endpoints performing no ownership or identity verification beyond matching this reconstructible key. This makes it possible for unauthenticated attackers to compute valid `edit_key` values for appointments belonging to other users and cancel or reschedule those appointments arbitrarily. Exploitation requires the `allow_cancellation` or `allow_rescheduling` setting to be enabled on the site, both of which are common configurations for active booking deployments; an attacker can obtain the inputs needed to reconstruct a victim's key by booking their own appointment to observe their sequential `client_id` and correlating publicly visible appointment times and enumerable staff identifiers.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=2.7.6
PublishedJuly 1, 2026
Last updatedJuly 2, 2026
Affected pluginwappointment

What Changed in the Fix

Changes introduced in v2.7.7

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-9188 (Wappointment) ## 1. Vulnerability Summary The **Wappointment** plugin (<= 2.7.6) for WordPress contains an **Insecure Direct Object Reference (IDOR)** vulnerability in its unauthenticated appointment management functionality. The authorization token (`e…

Show full research plan

Exploitation Research Plan - CVE-2026-9188 (Wappointment)

1. Vulnerability Summary

The Wappointment plugin (<= 2.7.6) for WordPress contains an Insecure Direct Object Reference (IDOR) vulnerability in its unauthenticated appointment management functionality. The authorization token (edit_key / appointmentkey) used to validate requests for cancelling or rescheduling appointments is generated using a predictable, unsalted MD5 hash of three enumerable or observable values: the client_id (sequential integer), the appointment start_at (Unix timestamp), and the staff_id (small enumerable integer). Because no secret salt or cryptographically secure random component is included in the hash, an unauthenticated attacker can reconstruct the valid key for any appointment and perform unauthorized actions.

2. Attack Vector Analysis

  • Endpoint: WordPress REST API endpoint used for appointment cancellation.
    • Path: /wp-json/wappointment/v1/appointment/cancel (inferred from Wappointment REST naming conventions).
  • HTTP Method: POST
  • Vulnerable Parameter: appointmentkey (maps to edit_key in the database).
  • Identity Parameter: id (the sequential appointment_id).
  • Authentication: None (Unauthenticated).
  • Preconditions:
    • The setting "Allow clients to cancel and reschedule appointments" must be enabled (Wappointment > Settings > General).
    • The attacker must know or guess the victim's appointment_id, client_id, and start_at timestamp.

3. Code Flow

  1. Key Generation (Creation Phase):
    • When an appointment is created, Wappointment\Models\Client::generateEditKey($start_at) or Wappointment\Models\Appointment\Recurrence::generateEditKey() is called.
    • Based on app/Models/Appointment/Recurrence.php, the key is generated as:
      md5($timestamp . $staff_id)
    • Based on the vulnerability description and app/Models/Client.php, the broader key construction used for standard bookings is:
      md5($client_id . $start_at . $staff_id) (or similar sequential concatenation).
  2. Access Control (Action Phase):
    • A request is sent to the cancellation endpoint (e.g., POST /wp-json/wappointment/v1/appointment/cancel).
    • The request is handled by a controller that calls tryCancel().
    • tryCancel() retrieves the appointment by id and compares the provided appointmentkey against the edit_key stored in the database.
    • If they match, the appointment is cancelled without further identity verification.

4. Nonce Acquisition Strategy

This specific REST endpoint is designed for unauthenticated users (e.g., clicking a link in a confirmation email) and typically does not require a WordPress CSRF nonce (_wpnonce) if configured as a public REST route.

However, if a nonce is required for the wp-json API generally:

  1. Identify the shortcode: [wappointment_form] (common for this plugin).
  2. Create a public page: wp post create --post_type=page --post_status=publish --post_content='[wappointment_form]'
  3. Navigate to the page and use browser_eval to extract the REST nonce usually localized by WordPress or the plugin:
    browser_eval("window.wpApiSettings?.nonce || window.wappointment_data?.nonce")

5. Exploitation Strategy

Step 1: Reconnaissance (Sequential ID Discovery)

  1. As the attacker, book a "probe" appointment.
  2. Note the returned appointment_id (e.g., 105) and client_id (e.g., 50).
  3. Observe the public booking calendar to find a victim's booked slot. Note the date/time (e.g., 2023-11-20 10:00:00).

Step 2: Key Reconstruction

  1. Predict client_id: If the attacker's ID was 50, and the victim booked shortly after, the victim's client_id is likely 51.
  2. Determine start_at: Convert the observed appointment time to a Unix timestamp.
    • 2023-11-20 10:00:00 -> 1700474400.
  3. Determine staff_id: Usually 1 for single-user setups, or enumerable via the booking form's staff selection dropdown.
  4. Compute MD5: echo -n "5117004744001" | md5sum (assuming client_id . start_at . staff_id).

Step 3: Payload Delivery

Send the cancellation request using http_request.

{
  "method": "POST",
  "url": "http://vulnerable-site.com/wp-json/wappointment/v1/appointment/cancel",
  "headers": {
    "Content-Type": "application/json"
  },
  "body": "{\"id\": 106, \"appointmentkey\": \"[RECONSTRUCTED_MD5]\"}"
}

6. Test Data Setup

  1. Configure Plugin:
    • Install Wappointment 2.7.6.
    • Create a Service and a Staff member (staff_id 1).
    • Enable cancellations in Wappointment > Settings > General.
  2. Create Victim Data:
    • Perform a booking as "Victim" (e.g., via the frontend form) for a specific time.
    • Record the appointment_id and client_id from the database to verify the attacker's "prediction".
  3. Create Attacker Data:
    • Perform a booking as "Attacker" to establish the sequential ID baseline.

7. Expected Results

  • Success: The server returns a 200 OK with a JSON response indicating success (e.g., {"success": true, "message": "Appointment cancelled"}).
  • Database Impact: The appointment record in the wappo_appointments table will have its status updated to cancelled (or the record will be soft-deleted).

8. Verification Steps

  1. Check via WP-CLI:
    wp db query "SELECT status FROM wp_wappo_appointments WHERE id = 106"
    
    Confirm the status is no longer confirmed or pending.
  2. UI Verification: Check the Wappointment admin dashboard; the victim's appointment should appear as cancelled or disappear from the active list.

9. Alternative Approaches

If md5(client_id . start_at . staff_id) fails:

  1. Try md5(start_at . staff_id) (as seen in Recurrence.php).
  2. Try md5(client_id . start_at) (as seen in Client.php).
  3. Brute-force the client_id (since it's a small sequential integer) by iterating through Attacker_Client_ID +/- 10 and sending requests for each. Since the hash is MD5 and the inputs are known, this is highly feasible.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Wappointment plugin for WordPress is vulnerable to an unauthenticated Insecure Direct Object Reference (IDOR) due to the use of predictable MD5 hashes for appointment authorization keys. Attackers can reconstruct these keys using publicly observable or enumerable data (sequential client IDs and appointment timestamps) to arbitrarily cancel or reschedule appointments belonging to other users.

Vulnerable Code

// app/Models/Client.php line 36
    public function generateEditKey($start_at)
    {
        return md5($this->id . $start_at);
    }

---

// app/Models/Appointment/Recurrence.php line 39
    public function generateEditKey($start_at)
    {
        return md5($start_at);
    }

---

// app/Models/Appointment/Recurrence.php line 51
        //generate new key 
        $data_new['edit_key'] = $this->generateEditKey($start_temp->timestamp . $this->master->staff_id);

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wappointment/2.7.6/app/Models/Appointment/Recurrence.php /home/deploy/wp-safety.org/data/plugin-versions/wappointment/2.7.7/app/Models/Appointment/Recurrence.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wappointment/2.7.6/app/Models/Appointment/Recurrence.php	2025-11-24 23:27:22.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wappointment/2.7.7/app/Models/Appointment/Recurrence.php	2026-05-24 14:04:58.000000000 +0000
@@ -38,7 +38,7 @@
     }
     public function generateEditKey($start_at)
     {
-        return md5($start_at);
+        return bin2hex(random_bytes(16));
     }
     private function generateForDay(Carbon $start_temp)
     {
diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wappointment/2.7.6/app/Models/Client.php /home/deploy/wp-safety.org/data/plugin-versions/wappointment/2.7.7/app/Models/Client.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wappointment/2.7.6/app/Models/Client.php	2026-01-18 19:07:20.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wappointment/2.7.7/app/Models/Client.php	2026-05-24 14:04:58.000000000 +0000
@@ -36,7 +36,7 @@
     }
     public function generateEditKey($start_at)
     {
-        return md5($this->id . $start_at);
+        return bin2hex(random_bytes(16));
     }

Exploit Outline

1. Reconnaissance: The attacker books a test appointment to determine the current sequential 'client_id' and 'appointment_id' baseline. 2. Victim Identification: The attacker identifies a target appointment on the public booking calendar and notes its 'start_at' Unix timestamp. 3. Key Reconstruction: Using the victim's predicted 'client_id' (sequential increment from the baseline), the 'start_at' timestamp, and the enumerable 'staff_id', the attacker computes the MD5 hash (e.g., md5(client_id . start_at)). 4. Unauthorized Request: The attacker sends an unauthenticated POST request to the plugin's REST API endpoint (e.g., /wp-json/wappointment/v1/appointment/cancel) with the victim's 'id' and the reconstructed 'appointmentkey'. 5. Success Verification: The server accepts the predicted key as valid authorization and cancels the appointment, returning a success response.

Check if your site is affected.

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