Appointment Bookings for Zoom GoogleMeet and more – Wappointment <= 2.7.6 - Unauthenticated Insecure Direct Object Reference via Predictable 'edit_key' / 'appointmentkey' Parameter
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:NTechnical Details
What Changed in the Fix
Changes introduced in v2.7.7
Source Code
WordPress.org SVN# 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).
- Path:
- HTTP Method:
POST - Vulnerable Parameter:
appointmentkey(maps toedit_keyin the database). - Identity Parameter:
id(the sequentialappointment_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, andstart_attimestamp.
3. Code Flow
- Key Generation (Creation Phase):
- When an appointment is created,
Wappointment\Models\Client::generateEditKey($start_at)orWappointment\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).
- When an appointment is created,
- 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 byidand compares the providedappointmentkeyagainst theedit_keystored in the database.- If they match, the appointment is cancelled without further identity verification.
- A request is sent to the cancellation endpoint (e.g.,
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:
- Identify the shortcode:
[wappointment_form](common for this plugin). - Create a public page:
wp post create --post_type=page --post_status=publish --post_content='[wappointment_form]' - Navigate to the page and use
browser_evalto 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)
- As the attacker, book a "probe" appointment.
- Note the returned
appointment_id(e.g.,105) andclient_id(e.g.,50). - 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
- Predict
client_id: If the attacker's ID was50, and the victim booked shortly after, the victim'sclient_idis likely51. - Determine
start_at: Convert the observed appointment time to a Unix timestamp.2023-11-20 10:00:00->1700474400.
- Determine
staff_id: Usually1for single-user setups, or enumerable via the booking form's staff selection dropdown. - Compute MD5:
echo -n "5117004744001" | md5sum(assumingclient_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
- Configure Plugin:
- Install Wappointment 2.7.6.
- Create a Service and a Staff member (
staff_id1). - Enable cancellations in Wappointment > Settings > General.
- Create Victim Data:
- Perform a booking as "Victim" (e.g., via the frontend form) for a specific time.
- Record the
appointment_idandclient_idfrom the database to verify the attacker's "prediction".
- Create Attacker Data:
- Perform a booking as "Attacker" to establish the sequential ID baseline.
7. Expected Results
- Success: The server returns a
200 OKwith a JSON response indicating success (e.g.,{"success": true, "message": "Appointment cancelled"}). - Database Impact: The appointment record in the
wappo_appointmentstable will have its status updated tocancelled(or the record will be soft-deleted).
8. Verification Steps
- Check via WP-CLI:
Confirm the status is no longerwp db query "SELECT status FROM wp_wappo_appointments WHERE id = 106"confirmedorpending. - 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:
- Try
md5(start_at . staff_id)(as seen inRecurrence.php). - Try
md5(client_id . start_at)(as seen inClient.php). - Brute-force the
client_id(since it's a small sequential integer) by iterating throughAttacker_Client_ID +/- 10and sending requests for each. Since the hash is MD5 and the inputs are known, this is highly feasible.
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
@@ -38,7 +38,7 @@ } public function generateEditKey($start_at) { - return md5($start_at); + return bin2hex(random_bytes(16)); } private function generateForDay(Carbon $start_temp) { @@ -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.