[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fpuqWOMZtJPsJR0D4d0wsdwcWVdivFkEb5o4BbnDTOuc":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-2026-10623","pressprimer-quiz-insecure-direct-object-reference-to-authenticated-custom-arbitrary-modification-via-quizid-itemid-and-r","PressPrimer Quiz \u003C= 2.3.0 - Insecure Direct Object Reference to Authenticated (Custom+) Arbitrary Modification via 'quiz_id', 'item_id', and 'rule_id' Parameters","The PressPrimer Quiz – AI Quiz Maker, Exam Builder & LMS Assessment Plugin plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 2.3.0 via the 'rule_id' parameter due to missing validation on a user controlled key. This makes it possible for authenticated attackers, with custom-level access and above, to modify or delete quiz rules belonging to other teachers, resulting in unauthorized tampering of another user's quiz structure.","pressprimer-quiz",null,"\u003C=2.3.0","2.3.1","medium",4.3,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:N","Authorization Bypass Through User-Controlled Key","2026-06-17 16:50:00","2026-06-18 05:34:26",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F150ac796-d77b-4915-8bbf-9f9b54be8eaf?source=api-prod",1,[22,23,24,25,26,27,28,29],"includes\u002Fapi\u002Fclass-ppq-rest-controller.php","languages\u002Fpressprimer-quiz.pot","pressprimer-quiz.php","readme.txt","vendor\u002Fcomposer\u002Finstalled.json","vendor\u002Fcomposer\u002Finstalled.php","vendor\u002Fsymfony\u002Fpolyfill-mbstring\u002FMbstring.php","vendor\u002Fsymfony\u002Fpolyfill-mbstring\u002Fbootstrap72.php","researched",false,3,"This exploitation research plan targets **CVE-2026-10623**, an Insecure Direct Object Reference (IDOR) vulnerability in the **PressPrimer Quiz** plugin. This vulnerability allows authenticated users with \"custom-level access\" (typically Instructor\u002FTeacher roles) to modify or delete quiz rules and items belonging to other users.\n\n### 1. Vulnerability Summary\n*   **Vulnerability:** Insecure Direct Object Reference (IDOR)\n*   **Location:** `includes\u002Fapi\u002Fclass-ppq-rest-controller.php`\n*   **Cause:** The REST API endpoints for updating and deleting quiz items and rules validate that the user has the general permission to manage quizzes (`check_permission`), but they fail to verify if the specific `quiz_id`, `item_id`, or `rule_id` belongs to a quiz owned by the current user.\n*   **Impact:** Unauthorized modification or deletion of quiz structures (questions, logic rules, etc.) across the entire WordPress installation.\n\n### 2. Attack Vector Analysis\n*   **Endpoints:**\n    *   `PUT \u002Fwp-json\u002Fppq\u002Fv1\u002Fquizzes\u002F(?P\u003Cquiz_id>\\d+)\u002Fitems\u002F(?P\u003Citem_id>\\d+)`\n    *   `DELETE \u002Fwp-json\u002Fppq\u002Fv1\u002Fquizzes\u002F(?P\u003Cquiz_id>\\d+)\u002Fitems\u002F(?P\u003Citem_id>\\d+)`\n    *   `PUT \u002Fwp-json\u002Fppq\u002Fv1\u002Fquizzes\u002F(?P\u003Cquiz_id>\\d+)\u002Frules\u002F(?P\u003Crule_id>\\d+)`\n    *   `DELETE \u002Fwp-json\u002Fppq\u002Fv1\u002Fquizzes\u002F(?P\u003Cquiz_id>\\d+)\u002Frules\u002F(?P\u003Crule_id>\\d+)`\n*   **Vulnerable Parameters:** `quiz_id`, `item_id`, `rule_id`.\n*   **Authentication:** Authenticated. The attacker needs a role that satisfies `check_permission`. In LMS contexts, this is often a custom role like \"Teacher\" or \"Instructor,\" or the standard \"Editor\" role.\n*   **Preconditions:** The attacker must know (or iterate through) the numeric IDs of quizzes and rules they do not own.\n\n### 3. Code Flow\n1.  **Registration:** `PressPrimer_Quiz_REST_Controller::register_routes()` registers the `\u002Fquizzes\u002F...` endpoints with `check_permission` as the `permission_callback`.\n2.  **Authorization Check:** `check_permission` (likely using `current_user_can('edit_posts')` or a custom capability) verifies the user's role globally but does not receive the request parameters to check object ownership.\n3.  **Callback Execution:** The callback (e.g., `update_quiz_rule` or `delete_quiz_rule`) is invoked.\n4.  **The Sink:** The callback retrieves the rule\u002Fitem from the database using the user-provided `rule_id` or `item_id` and performs the update\u002Fdelete operation without checking if the associated `quiz_id` is authored by the `get_current_user_id()`.\n\n### 4. Nonce Acquisition Strategy\nWordPress REST API requires a `wp_rest` nonce for non-GET requests when using cookie-based authentication.\n\n1.  **Identify Trigger:** The PressPrimer Quiz editor loads on the admin pages for Quizzes.\n2.  **Access Admin:** Authenticate as the attacker and navigate to the Quiz dashboard: `\u002Fwp-admin\u002Fadmin.php?page=ppq-quizzes`.\n3.  **Extraction:** The WordPress core or the plugin usually localizes the REST nonce for use in the editor's React\u002FVue frontend.\n4.  **Browser Eval:**\n    ```javascript\n    \u002F\u002F Standard WordPress REST nonce location\n    window.wpApiSettings?.nonce || window.ppq_editor_vars?.nonce\n    ```\n    *Note: `ppq_editor_vars` is an inferred name based on standard plugin naming conventions; the execution agent should check `window` for objects containing \"ppq\".*\n\n### 5. Exploitation Strategy\nThe goal is to delete a quiz rule belonging to a different user.\n\n**Step 1: Setup Victim Data**\nAs an Admin or Victim user, create a quiz and a rule. Record the `rule_id` and `quiz_id`.\n\n**Step 2: Attacker Authentication**\nAuthenticate as an \"Editor\" (Attacker) and obtain the `wp_rest` nonce.\n\n**Step 3: The Attack Request (Deletion)**\nSend a `DELETE` request to the victim's rule endpoint.\n*   **Tool:** `http_request`\n*   **Method:** `DELETE`\n*   **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fppq\u002Fv1\u002Fquizzes\u002F[VICTIM_QUIZ_ID]\u002Frules\u002F[VICTIM_RULE_ID]`\n*   **Headers:**\n    *   `Content-Type: application\u002Fjson`\n    *   `X-WP-Nonce: [EXTRACTED_NONCE]`\n*   **Body:** `{}`\n\n**Step 4: The Attack Request (Modification)**\nAlternatively, modify the rule to change the quiz logic.\n*   **Method:** `PUT`\n*   **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-json\u002Fppq\u002Fv1\u002Fquizzes\u002F[VICTIM_QUIZ_ID]\u002Frules\u002F[VICTIM_RULE_ID]`\n*   **Body:** `{\"rule_data\": {\"type\": \"modified\", \"logic\": \"always_fail\"}}` (Parameter names are inferred from common REST patterns; use the response from a `GET` request to a rule owned by the attacker to see the exact structure).\n\n### 6. Test Data Setup\n1.  **Users:**\n    *   `victim_teacher` (Role: Editor)\n    *   `attacker_teacher` (Role: Editor)\n2.  **Victim Content:**\n    *   Create a Quiz via WP-CLI or the UI as `victim_teacher`.\n    *   Add a \"Rule\" to that quiz. Rules are likely stored in a custom table (e.g., `wp_ppq_rules`).\n3.  **Discovery:**\n    *   Use `wp db query \"SELECT id, quiz_id FROM wp_ppq_rules LIMIT 1;\"` to find a valid target ID if not using the UI.\n\n### 7. Expected Results\n*   **Successful Deletion:** The REST API returns a `200 OK` or `204 No Content` response. A subsequent `GET` request to that ID returns a `404`.\n*   **Successful Modification:** The REST API returns `200 OK` with the modified object data.\n*   **Vulnerable State:** The operation completes successfully even though `attacker_teacher` does not own the quiz.\n\n### 8. Verification Steps\n1.  **Database Check:**\n    ```bash\n    wp db query \"SELECT * FROM wp_ppq_rules WHERE id = [VICTIM_RULE_ID];\"\n    ```\n    *If deleted, the query returns no results.*\n2.  **UI Check:** Log in as `victim_teacher` and verify the rule is missing from their quiz editor.\n\n### 9. Alternative Approaches\n*   **Item Modification:** Target `\u002Fquizzes\u002F{quiz_id}\u002Fitems\u002F{item_id}` instead. If rules are protected by an extra check, items (individual questions in the quiz) might not be.\n*   **Quiz Meta Modification:** Check if the main quiz endpoint `\u002Fquizzes\u002F(?P\u003Cid>\\d+)` (EDITABLE) also lacks ownership validation, which would allow changing the title, description, or settings of another user's quiz.","The PressPrimer Quiz plugin for WordPress is vulnerable to an Insecure Direct Object Reference (IDOR) via its REST API endpoints for quiz items and rules. Authenticated attackers with basic editing privileges (such as the 'Editor' role or custom 'Instructor' roles) can modify or delete quiz structures belonging to other users by manipulating the 'quiz_id', 'item_id', and 'rule_id' parameters in API requests.","\u002F\u002F includes\u002Fapi\u002Fclass-ppq-rest-controller.php @ 2.3.0 line 2480\npublic function update_quiz_item( $request ) {\n    $item_id = absint( $request['item_id'] );\n    $data    = $request->get_json_params();\n\n    $item = PressPrimer_Quiz_Quiz_Item::get( $item_id );\n\n    if ( ! $item ) {\n        return new WP_Error( 'not_found', __( 'Quiz item not found.', 'pressprimer-quiz' ), [ 'status' => 404 ] );\n    }\n\n    $item->update( $data );\n    \u002F\u002F ...\n}\n\n---\n\n\u002F\u002F includes\u002Fapi\u002Fclass-ppq-rest-controller.php @ 2.3.0 line 2617\npublic function update_quiz_rule( $request ) {\n    $rule_id = absint( $request['rule_id'] );\n    $data    = $request->get_json_params();\n\n    $rule = PressPrimer_Quiz_Quiz_Rule::get( $rule_id );\n\n    if ( ! $rule ) {\n        return new WP_Error( 'not_found', __( 'Quiz rule not found.', 'pressprimer-quiz' ), [ 'status' => 404 ] );\n    }\n\n    $rule->update( $data );\n    \u002F\u002F ...\n}","--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpressprimer-quiz\u002F2.3.0\u002Fincludes\u002Fapi\u002Fclass-ppq-rest-controller.php\t2026-06-03 16:46:06.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fpressprimer-quiz\u002F2.3.1\u002Fincludes\u002Fapi\u002Fclass-ppq-rest-controller.php\t2026-06-09 19:34:04.000000000 +0000\n@@ -479,6 +479,31 @@\n \t}\n \n \t\u002F**\n+\t * Load a quiz by ID and verify the current user owns it.\n+\t *\n+\t * Centralizes the ownership pattern so every per-quiz handler shares\n+\t * the same check. Admins with pressprimer_quiz_manage_all bypass the\n+\t * owner test. Returns 404 when the quiz does not exist and 403 when\n+\t * the requester does not own it.\n+\t *\n+\t * @since 2.3.1\n+\t *\n+\t * @param int $quiz_id Quiz ID.\n+\t * @return PressPrimer_Quiz_Quiz|WP_Error Quiz on success, error otherwise.\n+\t *\u002F\n+\tprivate function get_owned_quiz_or_error( $quiz_id ) {\n+\t\t$quiz = PressPrimer_Quiz_Quiz::get( absint( $quiz_id ) );\n+\t\tif ( ! $quiz ) {\n+\t\t\treturn new WP_Error( 'not_found', __( 'Quiz not found.', 'pressprimer-quiz' ), [ 'status' => 404 ] );\n+\t\t}\n+\t\tif ( ! current_user_can( 'pressprimer_quiz_manage_all' )\n+\t\t\t&& absint( $quiz->owner_id ) !== get_current_user_id() ) {\n+\t\t\treturn new WP_Error( 'forbidden', __( 'You do not have permission.', 'pressprimer-quiz' ), [ 'status' => 403 ] );\n+\t\t}\n+\t\treturn $quiz;\n+\t}\n+\n+\t\u002F**\n \t * Check editor permission for block usage\n \t *\n \t * Allows users who can edit posts to see the list of published quizzes\n@@ -2396,7 +2421,13 @@\n \t *\u002F\n \tpublic function get_quiz_items( $request ) {\n \t\t$quiz_id = absint( $request['quiz_id'] );\n-\t\t$items   = PressPrimer_Quiz_Quiz_Item::get_for_quiz( $quiz_id );\n+\n+\t\t$quiz = $this->get_owned_quiz_or_error( $quiz_id );\n+\t\tif ( is_wp_error( $quiz ) ) {\n+\t\t\treturn $quiz;\n+\t\t}\n+\n+\t\t$items = PressPrimer_Quiz_Quiz_Item::get_for_quiz( $quiz_id );\n \n \t\t$data = array_map(\n \t\t\tfunction ( $item ) {\n@@ -2438,6 +2469,11 @@\n \t\t$data         = $request->get_json_params();\n \t\t$question_ids = $data['question_ids'] ?? [];\n \n+\t\t$quiz = $this->get_owned_quiz_or_error( $quiz_id );\n+\t\tif ( is_wp_error( $quiz ) ) {\n+\t\t\treturn $quiz;\n+\t\t}\n+\n \t\tif ( empty( $question_ids ) ) {\n \t\t\treturn new WP_Error( 'no_questions', __( 'No questions provided.', 'pressprimer-quiz' ), [ 'status' => 400 ] );\n \t\t}\n@@ -2478,12 +2514,18 @@\n \t * @return WP_REST_Response|WP_Error Response object or error.\n \t *\u002F\n \tpublic function update_quiz_item( $request ) {\n+\t\t$quiz_id = absint( $request['quiz_id'] );\n \t\t$item_id = absint( $request['item_id'] );\n \t\t$data    = $request->get_json_params();\n \n+\t\t$quiz = $this->get_owned_quiz_or_error( $quiz_id );\n+\t\tif ( is_wp_error( $quiz ) ) {\n+\t\t\treturn $quiz;\n+\t\t}\n+\n \t\t$item = PressPrimer_Quiz_Quiz_Item::get( $item_id );\n \n-\t\tif ( ! $item ) {\n+\t\tif ( ! $item || absint( $item->quiz_id ) !== absint( $quiz->id ) ) {\n \t\t\treturn new WP_Error( 'not_found', __( 'Quiz item not found.', 'pressprimer-quiz' ), [ 'status' => 404 ] );\n \t\t}\n \n@@ -2505,11 +2547,17 @@\n \t * @return WP_REST_Response|WP_Error Response object or error.\n \t *\u002F\n \tpublic function delete_quiz_item( $request ) {\n+\t\t$quiz_id = absint( $request['quiz_id'] );\n \t\t$item_id = absint( $request['item_id'] );\n \n+\t\t$quiz = $this->get_owned_quiz_or_error( $quiz_id );\n+\t\tif ( is_wp_error( $quiz ) ) {\n+\t\t\treturn $quiz;\n+\t\t}\n+\n \t\t$item = PressPrimer_Quiz_Quiz_Item::get( $item_id );\n \n-\t\tif ( ! $item ) {\n+\t\tif ( ! $item || absint( $item->quiz_id ) !== absint( $quiz->id ) ) {\n \t\t\treturn new WP_Error( 'not_found', __( 'Quiz item not found.', 'pressprimer-quiz' ), [ 'status' => 404 ] );\n \t\t}\n \n@@ -2527,13 +2575,28 @@\n \t * @return WP_REST_Response|WP_Error Response object or error.\n \t *\u002F\n \tpublic function reorder_quiz_items( $request ) {\n+\t\t$quiz_id  = absint( $request['quiz_id'] );\n \t\t$data     = $request->get_json_params();\n \t\t$item_ids = $data['item_ids'] ?? [];\n \n+\t\t$quiz = $this->get_owned_quiz_or_error( $quiz_id );\n+\t\tif ( is_wp_error( $quiz ) ) {\n+\t\t\treturn $quiz;\n+\t\t}\n+\n \t\tif ( empty( $item_ids ) ) {\n \t\t\treturn new WP_Error( 'no_items', __( 'No items provided.', 'pressprimer-quiz' ), [ 'status' => 400 ] );\n \t\t}\n \n+\t\t\u002F\u002F Every supplied item must belong to the URL's quiz so the\n+\t\t\u002F\u002F reorder cannot reach across into another teacher's items.\n+\t\tforeach ( $item_ids as $item_id ) {\n+\t\t\t$item = PressPrimer_Quiz_Quiz_Item::get( absint( $item_id ) );\n+\t\t\tif ( ! $item || absint( $item->quiz_id ) !== absint( $quiz->id ) ) {\n+\t\t\t\treturn new WP_Error( 'not_found', __( 'Quiz item not found.', 'pressprimer-quiz' ), [ 'status' => 404 ] );\n+\t\t\t}\n+\t\t}\n+\n \t\t$result = PressPrimer_Quiz_Quiz_Item::reorder( $item_ids );\n \n \t\tif ( is_wp_error( $result ) ) {\n@@ -2553,7 +2616,13 @@\n \t *\u002F\n \tpublic function get_quiz_rules( $request ) {\n \t\t$quiz_id = absint( $request['quiz_id'] );\n-\t\t$rules   = PressPrimer_Quiz_Quiz_Rule::get_for_quiz( $quiz_id );\n+\n+\t\t$quiz = $this->get_owned_quiz_or_error( $quiz_id );\n+\t\tif ( is_wp_error( $quiz ) ) {\n+\t\t\treturn $quiz;\n+\t\t}\n+\n+\t\t$rules = PressPrimer_Quiz_Quiz_Rule::get_for_quiz( $quiz_id );\n \n \t\t$data = array_map(\n \t\t\tfunction ( $rule ) {\n@@ -2588,6 +2657,11 @@\n \t\t$quiz_id = absint( $request['quiz_id'] );\n \t\t$data    = $request->get_json_params();\n \n+\t\t$quiz = $this->get_owned_quiz_or_error( $quiz_id );\n+\t\tif ( is_wp_error( $quiz ) ) {\n+\t\t\treturn $quiz;\n+\t\t}\n+\n \t\t$rule_id = PressPrimer_Quiz_Quiz_Rule::create(\n \t\t\t[\n \t\t\t\t'quiz_id'        => $quiz_id,\n@@ -2615,12 +2689,18 @@\n \t * @return WP_REST_Response|WP_Error Response object or error.\n \t *\u002F\n \tpublic function update_quiz_rule( $request ) {\n+\t\t$quiz_id = absint( $request['quiz_id'] );\n \t\t$rule_id = absint( $request['rule_id'] );\n \t\t$data    = $request->get_json_params();\n \n+\t\t$quiz = $this->get_owned_quiz_or_error( $quiz_id );\n+\t\tif ( is_wp_error( $quiz ) ) {\n+\t\t\treturn $quiz;\n+\t\t}\n+\n \t\t$rule = PressPrimer_Quiz_Quiz_Rule::get( $rule_id );\n \n-\t\tif ( ! $rule ) {\n+\t\tif ( ! $rule || absint( $rule->quiz_id ) !== absint( $quiz->id ) ) {\n \t\t\treturn new WP_Error( 'not_found', __( 'Quiz rule not found.', 'pressprimer-quiz' ), [ 'status' => 404 ] );\n \t\t}\n \n@@ -2655,11 +2735,17 @@\n \t * @return WP_REST_Response|WP_Error Response object or error.\n \t *\u002F\n \tpublic function delete_quiz_rule( $request ) {\n+\t\t$quiz_id = absint( $request['quiz_id'] );\n \t\t$rule_id = absint( $request['rule_id'] );\n \n+\t\t$quiz = $this->get_owned_quiz_or_error( $quiz_id );\n+\t\tif ( is_wp_error( $quiz ) ) {\n+\t\t\treturn $quiz;\n+\t\t}\n+\n \t\t$rule = PressPrimer_Quiz_Quiz_Rule::get( $rule_id );\n \n-\t\tif ( ! $rule ) {\n+\t\tif ( ! $rule || absint( $rule->quiz_id ) !== absint( $quiz->id ) ) {\n \t\t\treturn new WP_Error( 'not_found', __( 'Quiz rule not found.', 'pressprimer-quiz' ), [ 'status' => 404 ] );\n \t\t}\n \n@@ -2677,16 +2763,31 @@\n \t * @return WP_REST_Response|WP_Error Response object or error.\n \t *\u002F\n \tpublic function reorder_quiz_rules( $request ) {\n+\t\t$quiz_id    = absint( $request['quiz_id'] );\n \t\t$data       = $request->get_json_params();\n \t\t$rule_order = isset( $data['rule_order'] ) && is_array( $data['rule_order'] ) ? $data['rule_order'] : [];\n \n+\t\t$quiz = $this->get_owned_quiz_or_error( $quiz_id );\n+\t\tif ( is_wp_error( $quiz ) ) {\n+\t\t\treturn $quiz;\n+\t\t}\n+\n \t\tif ( empty( $rule_order ) ) {\n \t\t\treturn new WP_Error( 'no_rules', __( 'No rules provided.', 'pressprimer-quiz' ), [ 'status' => 400 ] );\n \t\t}\n \n \t\t$order_map = [];\n \t\tforeach ( $rule_order as $index => $rule_id ) {\n-\t\t\t$order_map[ absint( $rule_id ) ] = (int) $index;\n+\t\t\t$rule_id = absint( $rule_id );\n+\n+\t\t\t\u002F\u002F Every supplied rule must belong to the URL's quiz so the\n+\t\t\t\u002F\u002F reorder cannot reach across into another teacher's rules.\n+\t\t\t$rule = PressPrimer_Quiz_Quiz_Rule::get( $rule_id );\n+\t\t\tif ( ! $rule || absint( $rule->quiz_id ) !== absint( $quiz->id ) ) {\n+\t\t\t\treturn new WP_Error( 'not_found', __( 'Quiz rule not found.', 'pressprimer-quiz' ), [ 'status' => 404 ] );\n+\t\t\t}\n+\n+\t\t\t$order_map[ $rule_id ] = (int) $index;\n \t\t}\n \n \t\t$result = PressPrimer_Quiz_Quiz_Rule::reorder( $order_map );","The exploit involves an authenticated attacker (with a role capable of editing quizzes, such as an Editor or custom LMS Instructor role) targeting quiz content that they do not own. \n\n1. Authenticate as a user with basic quiz management permissions and obtain a valid WordPress REST API nonce (X-WP-Nonce).\n2. Identify the target `quiz_id` and the specific `item_id` (question) or `rule_id` (logic rule) belonging to another user.\n3. Send a `DELETE` or `PUT` request to the following REST endpoints:\n   - Items: `\u002Fwp-json\u002Fppq\u002Fv1\u002Fquizzes\u002F{quiz_id}\u002Fitems\u002F{item_id}`\n   - Rules: `\u002Fwp-json\u002Fppq\u002Fv1\u002Fquizzes\u002F{quiz_id}\u002Frules\u002F{rule_id}`\n4. Because the vulnerable code only validates that the user has the general capability to manage quizzes (via the 'permission_callback'), but does not verify ownership of the specific IDs provided, the server will execute the modification or deletion as requested.","gemini-3-flash-preview","2026-06-25 21:51:05","2026-06-25 21:51:56",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","2.3.0","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpressprimer-quiz\u002Ftags\u002F2.3.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fpressprimer-quiz.2.3.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpressprimer-quiz\u002Ftags\u002F2.3.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fpressprimer-quiz.2.3.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fpressprimer-quiz\u002Ftags"]