[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fHPf2p5c3FJYpd9M_9MflC3As7A4NOUMn_uuAWCkn__w":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":29,"research_verified":30,"research_rounds_completed":31,"research_plan":32,"research_summary":33,"research_vulnerable_code":34,"research_fix_diff":35,"research_exploit_outline":36,"research_model_used":37,"research_started_at":38,"research_completed_at":39,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":30,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":30,"source_links":40},"CVE-2026-9188","appointment-bookings-for-zoom-googlemeet-and-more-wappointment-unauthenticated-insecure-direct-object-reference-via-pred","Appointment Bookings for Zoom GoogleMeet and more – Wappointment \u003C= 2.7.6 - Unauthenticated Insecure Direct Object Reference via Predictable 'edit_key' \u002F 'appointmentkey' Parameter","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.","wappointment",null,"\u003C=2.7.6","2.7.7","medium",5.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Authorization Bypass Through User-Controlled Key","2026-07-01 20:20:27","2026-07-02 08:33:04",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F07069f39-f892-4c19-8e0b-e5e17b1ffb21?source=api-prod",1,[22,23,24,25,26,27,28],"app\u002FModels\u002FAppointment\u002FRecurrence.php","app\u002FModels\u002FClient.php","app\u002FValidators\u002FHttpRequest\u002FBooking.php","dist\u002Fmain.aa33bf921e35c59a46dd.bundle.js","dist\u002Fmanifest.json","index.php","readme.txt","researched",false,3,"# Exploitation Research Plan - CVE-2026-9188 (Wappointment)\n\n## 1. Vulnerability Summary\nThe **Wappointment** plugin (\u003C= 2.7.6) for WordPress contains an **Insecure Direct Object Reference (IDOR)** vulnerability in its unauthenticated appointment management functionality. The authorization token (`edit_key` \u002F `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.\n\n## 2. Attack Vector Analysis\n*   **Endpoint:** WordPress REST API endpoint used for appointment cancellation.\n    *   **Path:** `\u002Fwp-json\u002Fwappointment\u002Fv1\u002Fappointment\u002Fcancel` (inferred from Wappointment REST naming conventions).\n*   **HTTP Method:** `POST`\n*   **Vulnerable Parameter:** `appointmentkey` (maps to `edit_key` in the database).\n*   **Identity Parameter:** `id` (the sequential `appointment_id`).\n*   **Authentication:** None (Unauthenticated).\n*   **Preconditions:**\n    *   The setting \"Allow clients to cancel and reschedule appointments\" must be enabled (Wappointment > Settings > General).\n    *   The attacker must know or guess the victim's `appointment_id`, `client_id`, and `start_at` timestamp.\n\n## 3. Code Flow\n1.  **Key Generation (Creation Phase):**\n    *   When an appointment is created, `Wappointment\\Models\\Client::generateEditKey($start_at)` or `Wappointment\\Models\\Appointment\\Recurrence::generateEditKey()` is called.\n    *   Based on `app\u002FModels\u002FAppointment\u002FRecurrence.php`, the key is generated as:\n        `md5($timestamp . $staff_id)`\n    *   Based on the vulnerability description and `app\u002FModels\u002FClient.php`, the broader key construction used for standard bookings is:\n        `md5($client_id . $start_at . $staff_id)` (or similar sequential concatenation).\n2.  **Access Control (Action Phase):**\n    *   A request is sent to the cancellation endpoint (e.g., `POST \u002Fwp-json\u002Fwappointment\u002Fv1\u002Fappointment\u002Fcancel`).\n    *   The request is handled by a controller that calls `tryCancel()`.\n    *   `tryCancel()` retrieves the appointment by `id` and compares the provided `appointmentkey` against the `edit_key` stored in the database.\n    *   If they match, the appointment is cancelled without further identity verification.\n\n## 4. Nonce Acquisition Strategy\nThis 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. \n\nHowever, if a nonce is required for the `wp-json` API generally:\n1.  Identify the shortcode: `[wappointment_form]` (common for this plugin).\n2.  Create a public page: `wp post create --post_type=page --post_status=publish --post_content='[wappointment_form]'`\n3.  Navigate to the page and use `browser_eval` to extract the REST nonce usually localized by WordPress or the plugin:\n    `browser_eval(\"window.wpApiSettings?.nonce || window.wappointment_data?.nonce\")`\n\n## 5. Exploitation Strategy\n### Step 1: Reconnaissance (Sequential ID Discovery)\n1.  As the attacker, book a \"probe\" appointment.\n2.  Note the returned `appointment_id` (e.g., `105`) and `client_id` (e.g., `50`).\n3.  Observe the public booking calendar to find a victim's booked slot. Note the date\u002Ftime (e.g., `2023-11-20 10:00:00`).\n\n### Step 2: Key Reconstruction\n1.  **Predict `client_id`:** If the attacker's ID was `50`, and the victim booked shortly after, the victim's `client_id` is likely `51`.\n2.  **Determine `start_at`:** Convert the observed appointment time to a Unix timestamp.\n    *   `2023-11-20 10:00:00` -> `1700474400`.\n3.  **Determine `staff_id`:** Usually `1` for single-user setups, or enumerable via the booking form's staff selection dropdown.\n4.  **Compute MD5:** `echo -n \"5117004744001\" | md5sum` (assuming `client_id . start_at . staff_id`).\n\n### Step 3: Payload Delivery\nSend the cancellation request using `http_request`.\n\n```json\n{\n  \"method\": \"POST\",\n  \"url\": \"http:\u002F\u002Fvulnerable-site.com\u002Fwp-json\u002Fwappointment\u002Fv1\u002Fappointment\u002Fcancel\",\n  \"headers\": {\n    \"Content-Type\": \"application\u002Fjson\"\n  },\n  \"body\": \"{\\\"id\\\": 106, \\\"appointmentkey\\\": \\\"[RECONSTRUCTED_MD5]\\\"}\"\n}\n```\n\n## 6. Test Data Setup\n1.  **Configure Plugin:**\n    *   Install Wappointment 2.7.6.\n    *   Create a Service and a Staff member (`staff_id` 1).\n    *   Enable cancellations in **Wappointment > Settings > General**.\n2.  **Create Victim Data:**\n    *   Perform a booking as \"Victim\" (e.g., via the frontend form) for a specific time.\n    *   Record the `appointment_id` and `client_id` from the database to verify the attacker's \"prediction\".\n3.  **Create Attacker Data:**\n    *   Perform a booking as \"Attacker\" to establish the sequential ID baseline.\n\n## 7. Expected Results\n*   **Success:** The server returns a `200 OK` with a JSON response indicating success (e.g., `{\"success\": true, \"message\": \"Appointment cancelled\"}`).\n*   **Database Impact:** The appointment record in the `wappo_appointments` table will have its status updated to `cancelled` (or the record will be soft-deleted).\n\n## 8. Verification Steps\n1.  **Check via WP-CLI:**\n    ```bash\n    wp db query \"SELECT status FROM wp_wappo_appointments WHERE id = 106\"\n    ```\n    Confirm the status is no longer `confirmed` or `pending`.\n2.  **UI Verification:** Check the Wappointment admin dashboard; the victim's appointment should appear as cancelled or disappear from the active list.\n\n## 9. Alternative Approaches\nIf `md5(client_id . start_at . staff_id)` fails:\n1.  Try `md5(start_at . staff_id)` (as seen in `Recurrence.php`).\n2.  Try `md5(client_id . start_at)` (as seen in `Client.php`).\n3.  Brute-force the `client_id` (since it's a small sequential integer) by iterating through `Attacker_Client_ID +\u002F- 10` and sending requests for each. Since the hash is MD5 and the inputs are known, this is highly feasible.","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.","\u002F\u002F app\u002FModels\u002FClient.php line 36\n    public function generateEditKey($start_at)\n    {\n        return md5($this->id . $start_at);\n    }\n\n---\n\n\u002F\u002F app\u002FModels\u002FAppointment\u002FRecurrence.php line 39\n    public function generateEditKey($start_at)\n    {\n        return md5($start_at);\n    }\n\n---\n\n\u002F\u002F app\u002FModels\u002FAppointment\u002FRecurrence.php line 51\n        \u002F\u002Fgenerate new key \n        $data_new['edit_key'] = $this->generateEditKey($start_temp->timestamp . $this->master->staff_id);","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwappointment\u002F2.7.6\u002Fapp\u002FModels\u002FAppointment\u002FRecurrence.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwappointment\u002F2.7.7\u002Fapp\u002FModels\u002FAppointment\u002FRecurrence.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwappointment\u002F2.7.6\u002Fapp\u002FModels\u002FAppointment\u002FRecurrence.php\t2025-11-24 23:27:22.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwappointment\u002F2.7.7\u002Fapp\u002FModels\u002FAppointment\u002FRecurrence.php\t2026-05-24 14:04:58.000000000 +0000\n@@ -38,7 +38,7 @@\n     }\n     public function generateEditKey($start_at)\n     {\n-        return md5($start_at);\n+        return bin2hex(random_bytes(16));\n     }\n     private function generateForDay(Carbon $start_temp)\n     {\ndiff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwappointment\u002F2.7.6\u002Fapp\u002FModels\u002FClient.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwappointment\u002F2.7.7\u002Fapp\u002FModels\u002FClient.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwappointment\u002F2.7.6\u002Fapp\u002FModels\u002FClient.php\t2026-01-18 19:07:20.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwappointment\u002F2.7.7\u002Fapp\u002FModels\u002FClient.php\t2026-05-24 14:04:58.000000000 +0000\n@@ -36,7 +36,7 @@\n     }\n     public function generateEditKey($start_at)\n     {\n-        return md5($this->id . $start_at);\n+        return bin2hex(random_bytes(16));\n     }","1. Reconnaissance: The attacker books a test appointment to determine the current sequential 'client_id' and 'appointment_id' baseline. \n2. Victim Identification: The attacker identifies a target appointment on the public booking calendar and notes its 'start_at' Unix timestamp.\n3. 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)).\n4. Unauthorized Request: The attacker sends an unauthenticated POST request to the plugin's REST API endpoint (e.g., \u002Fwp-json\u002Fwappointment\u002Fv1\u002Fappointment\u002Fcancel) with the victim's 'id' and the reconstructed 'appointmentkey'.\n5. Success Verification: The server accepts the predicted key as valid authorization and cancels the appointment, returning a success response.","gemini-3-flash-preview","2026-07-25 11:10:32","2026-07-25 11:11:16",{"type":41,"vulnerable_version":42,"fixed_version":11,"vulnerable_browse":43,"vulnerable_zip":44,"fixed_browse":45,"fixed_zip":46,"all_tags":47},"plugin","2.7.6","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwappointment\u002Ftags\u002F2.7.6","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwappointment.2.7.6.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwappointment\u002Ftags\u002F2.7.7","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwappointment.2.7.7.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwappointment\u002Ftags"]