[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fblKRSLZQr-w9dUKUklYOaiI8Yq2SzeTTTN6FRn_obII":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":9,"research_vulnerable_code":9,"research_fix_diff":9,"research_exploit_outline":9,"research_model_used":34,"research_started_at":35,"research_completed_at":36,"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,"source_links":37},"CVE-2026-3358","tutor-lms-missing-authorization-to-authenticated-subscriber-unauthorized-private-course-enrollment","Tutor LMS \u003C= 3.9.7 - Missing Authorization to Authenticated (Subscriber+) Unauthorized Private Course Enrollment","The Tutor LMS – eLearning and online course solution plugin for WordPress is vulnerable to unauthorized private course enrollment in all versions up to, and including, 3.9.7. This is due to missing post_status validation in the `enroll_now()` and `course_enrollment()` functions. Both enrollment endpoints verify the nonce, user authentication, and whether the course is purchasable, but fail to check if the course has a `private` post_status. This makes it possible for authenticated attackers with Subscriber-level access or above to enroll in private courses by sending a crafted POST request with the target course ID. The enrollment record is created in the database and the private course title and enrollment status are exposed in the subscriber's dashboard, though WordPress core access control prevents the subscriber from viewing the actual course content (returns 404). Enrollment in private courses should be restricted to users with the `read_private_posts` capability.","tutor",null,"\u003C=3.9.7","3.9.8","medium",5.4,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:L\u002FUI:N\u002FS:U\u002FC:L\u002FI:L\u002FA:N","Missing Authorization","2026-04-10 11:46:32","2026-04-11 01:24:57",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F0c173356-7228-4253-bb28-2c2e11af76fd?source=api-prod",1,[22,23,24,25,26,27,28,29],"assets\u002Fcss\u002Ftutor-admin.min.css","assets\u002Fcss\u002Ftutor-front.min.css","assets\u002Fcss\u002Ftutor-frontend-dashboard.min.css","assets\u002Fcss\u002Ftutor-rtl.min.css","assets\u002Fcss\u002Ftutor.min.css","assets\u002Fjs\u002Flazy-chunks\u002Ftutor-coupon-main-content.js","assets\u002Fjs\u002Flazy-chunks\u002Ftutor-course-builder-additional.js","assets\u002Fjs\u002Flazy-chunks\u002Ftutor-course-builder-basic.js","researched",false,3,"This analysis is based on the provided vulnerability description and the architectural patterns of the **Tutor LMS** plugin. Since the core PHP logic files were not provided in the snippet, the specific function locations and hooks are grounded in the CVE description and established Tutor LMS source patterns.\n\n### 1. Vulnerability Summary\nThe vulnerability is a **Missing Authorization** (specifically, missing object status validation) within the enrollment logic of Tutor LMS. When a user attempts to enroll in a course, the functions `enroll_now()` and `course_enrollment()` (likely located in `classes\u002FCourse.php` or similar logic handlers) verify that the user is authenticated and that the request is not a Cross-Site Request Forgery (CSRF) via a nonce check. \n\nHowever, they fail to verify the `post_status` of the target `course_id`. In WordPress, \"Private\" posts are intended to be hidden from everyone except those with the `read_private_posts` capability (typically Admins and Editors). By failing to check if a course is `private`, the plugin allows any authenticated user (Subscriber level) to create an enrollment record for a restricted course, effectively \"joining\" a class they should not even be able to see.\n\n### 2. Attack Vector Analysis\n*   **Endpoint:** WordPress AJAX interface (`\u002Fwp-admin\u002Fadmin-ajax.php`) or the `admin-post.php` handler.\n*   **Action:** The likely AJAX action is `tutor_enroll_now` or a direct POST to the enrollment handler.\n*   **Parameters:**\n    *   `course_id`: (Integer) The ID of the private course.\n    *   `tutor_nonce` or `_wpnonce`: (String) A valid nonce for the enrollment action.\n*   **Authentication:** Subscriber-level (or any authenticated user) is required.\n*   **Preconditions:** A course must exist with the `post_status` set to `private`.\n\n### 3. Code Flow (Inferred)\n1.  **Entry Point:** User triggers an enrollment request, usually via a POST request to `admin-ajax.php` with `action=tutor_enroll_now`.\n2.  **Nonce Verification:** The handler calls `check_ajax_referer('tutor_nonce', 'tutor_nonce')` or `wp_verify_nonce()`.\n3.  **Authentication Check:** The handler verifies `is_user_logged_in()`.\n4.  **Purchasability Check:** The code calls a helper like `tutor_utils()->is_course_purchasable($course_id)`. For many \"Private\" courses that are not connected to WooCommerce\u002FEDD, this may return `false` (meaning it's free\u002Fdirect), allowing the flow to proceed.\n5.  **The Flaw:** The code proceeds to call `tutor_utils()->do_enroll($course_id)` **without** checking if `get_post_status($course_id) === 'private'`.\n6.  **Sink:** A record is inserted into the `{wpdb->prefix}tutor_enrolled` database table.\n\n### 4. Nonce Acquisition Strategy\nTutor LMS localizes its security tokens into a global JavaScript object available on most frontend pages where Tutor LMS components are active (e.g., the Dashboard or Course Archive).\n\n*   **Localization Key:** `tutor_get_conf`\n*   **Nonce Path:** `tutor_get_conf.nonce`\n*   **Strategy:**\n    1.  **Identify Trigger:** The `[tutor_dashboard]` shortcode is the most reliable way to ensure all Tutor LMS frontend scripts and nonces are loaded.\n    2.  **Setup Page:** Create a temporary page containing this shortcode.\n    3.  **Acquisition:**\n        *   Log in as a Subscriber.\n        *   Navigate to the created page.\n        *   Execute: `browser_eval(\"tutor_get_conf.nonce\")`.\n\n### 5. Exploitation Strategy\nThis plan uses the `http_request` tool to simulate a Subscriber enrolling in a Private course.\n\n**Step 1: Discover\u002FIdentify Private Course ID**\nThe attacker needs the ID of a private course. In a test environment, this is created during setup. In a real scenario, this might be found through ID enumeration or information leaks.\n\n**Step 2: Obtain Nonce**\nAs the Subscriber, navigate to the Tutor Dashboard and extract the nonce.\n\n**Step 3: Execute Unauthorized Enrollment**\nPerform a POST request to the AJAX endpoint.\n\n*   **URL:** `http:\u002F\u002Flocalhost:8080\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **Method:** `POST`\n*   **Headers:** `Content-Type: application\u002Fx-www-form-urlencoded`\n*   **Body:**\n    ```\n    action=tutor_enroll_now&course_id=[PRIVATE_COURSE_ID]&tutor_nonce=[EXTRACTED_NONCE]\n    ```\n\n### 6. Test Data Setup\nTo be performed via `wp_cli`:\n\n1.  **Create Private Course:**\n    ```bash\n    wp post create --post_type=courses --post_title=\"Top Secret Course\" --post_status=private --post_author=1\n    # Note the resulting ID (e.g., 123)\n    ```\n2.  **Create Subscriber User:**\n    ```bash\n    wp user create attacker attacker@example.com --role=subscriber --user_pass=password\n    ```\n3.  **Create Nonce Source Page:**\n    ```bash\n    wp post create --post_type=page --post_title=\"Dashboard\" --post_status=publish --post_content='[tutor_dashboard]'\n    ```\n\n### 7. Expected Results\n*   **HTTP Response:** The server should return a JSON success message (e.g., `{\"success\":true,...}`) or a redirect to the course page.\n*   **Dashboard Exposure:** If the attacker visits their Subscriber dashboard, the \"Top Secret Course\" will now appear in their \"Enrolled Courses\" list.\n*   **Database State:** A new entry will exist in the `wp_tutor_enrolled` table linking the `attacker` user ID to the `private` course ID.\n\n### 8. Verification Steps\nAfter the exploit attempt, verify the unauthorized enrollment via `wp_cli`:\n\n```bash\n# Check the tutor_enrolled table directly\nwp db query \"SELECT * FROM wp_tutor_enrolled WHERE course_id = [PRIVATE_COURSE_ID];\"\n\n# Verify if the user (ID 2) is now enrolled\nwp db query \"SELECT user_id, course_id FROM wp_tutor_enrolled WHERE user_id = 2 AND course_id = [PRIVATE_COURSE_ID];\"\n```\nIf a row exists for the Subscriber user and the Private course, the exploitation is confirmed.\n\n### 9. Alternative Approaches\nIf the `tutor_enroll_now` AJAX action is restricted or behaves differently in the specific version:\n1.  **Direct POST:** Try sending the request directly to a course URL with `tutor_enroll_now=true` and the required nonce and ID as query parameters or POST body.\n2.  **Course Enrollment Action:** Check for the `course_enrollment` action (mentioned in the CVE description), which might be tied to a different hook like `admin_post_tutor_course_enrollment`.\n3.  **Variable Guessing:** If `tutor_nonce` fails, check the localized JS in `assets\u002Fjs\u002Flazy-chunks\u002F` for other potential nonce identifiers used in the enrollment flow. (e.g., searching for `nonce` in `tutor-course-builder-basic.js` or similar frontend logic).","gemini-3-flash-preview","2026-04-16 16:10:50","2026-04-16 16:11:20",{"type":38,"vulnerable_version":39,"fixed_version":11,"vulnerable_browse":40,"vulnerable_zip":41,"fixed_browse":42,"fixed_zip":43,"all_tags":44},"plugin","3.9.7","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ftutor\u002Ftags\u002F3.9.7","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Ftutor.3.9.7.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ftutor\u002Ftags\u002F3.9.8","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Ftutor.3.9.8.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Ftutor\u002Ftags"]