[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f0oa2DCoUIKpuprGNABo6c5B3AGueCHYzryxQ-p2EkiU":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-13934","tutor-lms-elearning-and-online-course-solution-missing-authorization-to-authenticated-subscriber-course-enrollment-bypas","Tutor LMS – eLearning and online course solution \u003C= 3.9.3 - Missing Authorization to Authenticated (Subscriber+) Course Enrollment Bypass","The Tutor LMS – eLearning and online course solution plugin for WordPress is vulnerable to unauthorized course enrollment in all versions up to, and including, 3.9.3. This is due to a missing capability check and purchasability validation in the `course_enrollment()` AJAX handler. This makes it possible for authenticated attackers, with subscriber level access and above, to enroll themselves in any course without going through the proper purchase flow.","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 18:43:50","2026-01-09 07:22:31",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F5de212c9-5c2e-4713-b1ce-022dd84520c3?source=api-prod",1,[],"researched",false,3,"# Exploitation Research Plan: CVE-2025-13934 (Tutor LMS Course Enrollment Bypass)\n\n## 1. Vulnerability Summary\nThe **Tutor LMS** plugin (\u003C= 3.9.3) contains a missing authorization vulnerability in its AJAX handler for course enrollment. The function `course_enrollment()` fails to verify if the current user has the right to enroll in a specific course (e.g., checking if a course is paid vs. free) or if they have bypass capabilities. This allows any authenticated user (Subscriber level and above) to enroll in any course, including premium\u002Fpaid courses, without completing the WooCommerce or internal purchase flow.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Action:** `tutor_course_enrollment`\n- **Vulnerable Function:** `tutor_course_enrollment()` (likely located in `classes\u002FAjax.php` or `includes\u002Ftutor-functions.php`)\n- **HTTP Method:** `POST`\n- **Parameters:**\n    - `action`: `tutor_course_enrollment`\n    - `course_id`: The ID of the target course (e.g., a paid course).\n    - `_wpnonce`: A valid WordPress nonce for the action.\n- **Authentication:** Required (Subscriber or higher).\n- **Preconditions:** At least one course must exist that is not \"Free\" or requires a purchase flow.\n\n## 3. Code Flow (Inferred from Patch Description)\n1.  **Entry:** The user triggers an AJAX request with `action=tutor_course_enrollment`.\n2.  **Hook Registration:** The plugin registers the action via:\n    `add_action('wp_ajax_tutor_course_enrollment', array($this, 'course_enrollment'));`\n3.  **Vulnerable Handler:** Inside `course_enrollment()`:\n    - The code retrieves `course_id` from `$_POST`.\n    - It performs a nonce check (e.g., `check_ajax_referer('tutor_nonce', '_wpnonce')`).\n    - **Missing Check:** It fails to call a validation function like `tutor_utils()->is_course_purchasable($course_id)` or check if the user has already paid.\n4.  **Sink:** It calls an internal enrollment function (e.g., `tutor_utils()->do_enroll($course_id, $user_id)`) which creates a record in the `wp_posts` table (post_type `tutor_enrolled`) or a custom table, linking the user to the course.\n\n## 4. Nonce Acquisition Strategy\nTutor LMS typically enqueues its nonces via `wp_localize_script` for use in its frontend JS. The nonce is usually attached to the `tutor_get_conf` or `_tutor_nonce` variable.\n\n1.  **Identify Trigger:** The enrollment script is loaded on Single Course pages.\n2.  **Creation:** No special setup is needed beyond having a course published.\n3.  **Access Page:** Navigate to any course page (even the one you want to exploit).\n4.  **Browser Evaluation:**\n    Use the `browser_eval` tool to extract the nonce:\n    ```javascript\n    \u002F\u002F Tutor LMS typically stores the nonce here:\n    window.tutor_get_conf?.nonce || window._tutor_get_conf?.nonce\n    ```\n    If not found, search the HTML source for `_wpnonce` or `tutor_nonce`.\n\n## 5. Exploitation Strategy\n1.  **Authentication:** Log in as a Subscriber user.\n2.  **Target Selection:** Identify the ID of a \"Paid\" course (not yet enrolled).\n3.  **Nonce Retrieval:**\n    - Navigate to the course page: `POST_ID_OF_COURSE`.\n    - Extract the nonce from the JS context.\n4.  **Execution:**\n    Send a POST request to `admin-ajax.php`:\n    - **URL:** `http:\u002F\u002F\u003Ctarget>\u002Fwp-admin\u002Fadmin-ajax.php`\n    - **Method:** `POST`\n    - **Content-Type:** `application\u002Fx-www-form-urlencoded`\n    - **Body:** `action=tutor_course_enrollment&course_id=\u003CTARGET_COURSE_ID>&_wpnonce=\u003CNONCE>`\n5.  **Expected Response:** A JSON object indicating success, e.g., `{\"success\": true, \"data\": ...}` or a redirect URL to the \"start-learning\" page.\n\n## 6. Test Data Setup\n1.  **Administrator Actions:**\n    - Install and activate **Tutor LMS**.\n    - (Optional but recommended) Install **WooCommerce** to enable \"Paid\" course status.\n    - Create a Course (ID: `X`):\n        - Title: \"Premium Hacking Course\"\n        - Price: Set to \"Paid\" via WooCommerce product linking.\n    - Create a Course (ID: `Y`):\n        - Title: \"Free Intro\"\n    - Create a user `subscriber_user` with the **Subscriber** role.\n2.  **Confirm Baseline:** Verify `subscriber_user` cannot access \"Premium Hacking Course\" (shows \"Enrol Now\" button leading to cart\u002Fcheckout).\n\n## 7. Expected Results\n- The AJAX request should return a successful status.\n- The user `subscriber_user` will now have \"Enrolled\" status for the premium course.\n- Navigating to the course URL will now show the \"Start Learning\" or \"Continue Lesson\" button instead of the purchase button.\n\n## 8. Verification Steps\n1.  **WP-CLI Check:**\n    Check for the enrollment record. In Tutor LMS, enrollments are often stored as a custom post type `tutor_enrolled` where `post_author` is the user ID and `post_parent` is the course ID.\n    ```bash\n    wp post list --post_type=tutor_enrolled --author=$(wp user get subscriber_user --field=ID) --post_parent=\u003CTARGET_COURSE_ID>\n    ```\n2.  **Database Check:**\n    ```bash\n    wp db query \"SELECT * FROM wp_posts WHERE post_type='tutor_enrolled' AND post_author=\u003CUSER_ID> AND post_parent=\u003CCOURSE_ID>\"\n    ```\n3.  **UI Check:** Use `browser_navigate` to visit the course page as the Subscriber and check if lessons are accessible.\n\n## 9. Alternative Approaches\n- **Missing Nonce:** If the `_wpnonce` check is also weak or uses a generic action, try using a nonce from a different Tutor LMS frontend component.\n- **REST API:** Check if `wp-json\u002Ftutor\u002Fv1\u002Fenroll` exists, as Tutor LMS is moving towards REST routes which might mirror the same authorization logic flaw.\n- **Parameter Variation:** If `course_id` fails, check for `course_id[]` or `id` (some handlers use inconsistent naming).","The Tutor LMS plugin for WordPress fails to validate course purchasability in its direct enrollment AJAX handler. This allows authenticated users, such as subscribers, to bypass payment flows and enroll in premium courses by sending a specifically crafted request to the 'tutor_course_enrollment' action.","\u002F\u002F In Tutor LMS \u003C= 3.9.3 - typically found in classes\u002FAjax.php\npublic function course_enrollment() {\n    tutor_utils()->checking_nonce();\n\n    $course_id = isset($_POST['course_id']) ? (int) $_POST['course_id'] : 0;\n    \n    \u002F\u002F VULNERABILITY: There is no check here to determine if the course is 'Free' \n    \u002F\u002F or if the user has already purchased the course through a proper gateway.\n    $enroll = tutor_utils()->do_enroll($course_id);\n\n    if ($enroll) {\n        wp_send_json_success();\n    } else {\n        wp_send_json_error();\n    }\n}","--- a\u002Fclasses\u002FAjax.php\n+++ b\u002Fclasses\u002FAjax.php\n@@ -205,6 +205,11 @@\n         tutor_utils()->checking_nonce();\n \n         $course_id = isset( $_POST['course_id'] ) ? (int) $_POST['course_id'] : 0;\n+\n+        \u002F\u002F Added authorization and purchasability check\n+        if ( ! tutor_utils()->is_course_free( $course_id ) ) {\n+            wp_send_json_error( array( 'message' => __( 'Direct enrollment is not allowed for this course.', 'tutor' ) ) );\n+        }\n \n         $enrolled = tutor_utils()->do_enroll( $course_id );","1. Log in as a Subscriber-level user on the target WordPress site.\n2. Identify the Post ID of a premium\u002Fpaid course that you are not enrolled in.\n3. Extract a valid AJAX nonce from the frontend course page; this is usually available in the JavaScript variable 'window._tutor_get_conf.nonce'.\n4. Send a POST request to '\u002Fwp-admin\u002Fadmin-ajax.php' with the parameters: 'action=tutor_course_enrollment', 'course_id=[TARGET_ID]', and '_wpnonce=[EXTRACTED_NONCE]'.\n5. Upon a successful JSON response, the user will be recorded as enrolled in the 'wp_posts' table (as a 'tutor_enrolled' post type), granting immediate access to the paid lessons without requiring payment.","gemini-3-flash-preview","2026-05-05 13:36:22","2026-05-05 13:38:06",{"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"]