CVE-2026-25372

Academy LMS <= 3.5.3 - Missing Authorization

mediumMissing Authorization
4.3
CVSS Score
4.3
CVSS Score
medium
Severity
3.5.4
Patched in
8d
Time to patch

Description

The Academy LMS plugin for WordPress is vulnerable to unauthorized access due to a missing capability check on a function in versions up to, and including, 3.5.3. This makes it possible for authenticated attackers, with instructor-level access and above, to perform an unauthorized action.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.5.3
PublishedFebruary 17, 2026
Last updatedFebruary 24, 2026
Affected pluginacademy

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-25372 ## 1. Vulnerability Summary The **Academy LMS** plugin (versions <= 3.5.3) is vulnerable to **Missing Authorization**. The vulnerability exists in one of the plugin's AJAX handlers—likely related to global settings or instructor management—where a capab…

Show full research plan

Exploitation Research Plan - CVE-2026-25372

1. Vulnerability Summary

The Academy LMS plugin (versions <= 3.5.3) is vulnerable to Missing Authorization. The vulnerability exists in one of the plugin's AJAX handlers—likely related to global settings or instructor management—where a capability check is either missing or insufficiently restrictive. While the handler may verify a WordPress nonce, it fails to ensure the user has administrative privileges (manage_options). This allows an authenticated user with Instructor level permissions to perform actions intended only for Administrators, such as modifying plugin configurations.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: academy_update_options (inferred based on plugin architecture) or academy_instructor_status_update (inferred).
  • HTTP Method: POST
  • Authentication: Required (Role: instructor).
  • Vulnerable Parameter: Parameters within the POST body used to define option names and values (e.g., option_name, option_value, or a settings array).
  • Preconditions: An attacker must have an account with the instructor role.

3. Code Flow

  1. Entry Point: The plugin registers AJAX actions in includes/Core/AJAX.php (or a similar class-based structure like Academy\Core\AJAX).
  2. Registration:
    add_action( 'wp_ajax_academy_update_options', [ $this, 'academy_update_options' ] );
    
  3. Vulnerable Function: The callback academy_update_options is invoked.
  4. Logic:
    • It retrieves the nonce from $_POST['nonce'] or $_POST['_wpnonce'].
    • It calls check_ajax_referer( 'academy_nonce', 'nonce' ).
    • The Flaw: It may check is_user_logged_in() or check if the user is an instructor, but it misses current_user_can( 'manage_options' ).
  5. Sink: The function calls update_option() or a wrapper function to save data to the wp_options table.

4. Nonce Acquisition Strategy

The Academy LMS plugin localizes administrative data for its JavaScript components. An instructor can find the necessary nonce by accessing their instructor dashboard.

  1. Shortcode: Identify the instructor dashboard page. If it doesn't exist, create one:
    • wp post create --post_type=page --post_title="Dashboard" --post_status=publish --post_content='[academy_instructor_dashboard]'
  2. Navigation: Navigate to the dashboard page as a logged-in instructor.
  3. Extraction: Use browser_eval to extract the nonce from the global JavaScript object:
    • Variable Name: academy_admin_ajax or academy_data (inferred from plugin source).
    • Key: nonce.
    • Command: browser_eval("window.academy_admin_ajax?.nonce").

5. Exploitation Strategy

The goal is to modify a global plugin setting (e.g., changing the instructor registration requirements or email settings) from an instructor account.

Step 1: Authentication

Authenticate as a user with the instructor role.

Step 2: Nonce Extraction

Retrieve the nonce using the strategy defined in Section 4.

Step 3: Malicious Request

Send a POST request to admin-ajax.php to update a restricted option.

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    action=academy_update_options&nonce=[NONCE]&option_name=academy_instructor_registration&option_value=auto
    
    (Note: option_name and option_value are inferred; the plugin may use an array like settings[option_name]=value).

Step 4: Verification

Confirm the option was updated in the database.

6. Test Data Setup

  1. Install Plugin: Academy LMS <= 3.5.3.
  2. Create Instructor:
    • wp user create attacker attacker@example.com --role=subscriber (Note: Academy LMS usually maps its own "Instructor" role; ensure the user has the instructor role via the plugin's settings).
    • Use wp user set-role attacker instructor if the role is available.
  3. Identify Setting: Check current value of a target setting:
    • wp option get academy_instructor_registration (Example setting).

7. Expected Results

  • Response: The server returns a successful JSON response (e.g., {"success": true}).
  • Impact: The value of the global setting academy_instructor_registration is changed in the database.
  • Unauthorized Action: An instructor has successfully modified a site-wide configuration that should be restricted to administrators.

8. Verification Steps

After sending the HTTP request, use WP-CLI to verify the change:

wp option get academy_instructor_registration

If the value matches the one sent in the option_value parameter, the exploitation is successful.

9. Alternative Approaches

If academy_update_options is not the vulnerable action:

  1. Instructor Status Update: Try action=academy_update_instructor_status. An instructor might be able to change their own status or the status of others.
    • Payload: action=academy_update_instructor_status&instructor_id=[MY_ID]&status=approved.
  2. Course Management: Try action=academy_delete_course on a course_id that does not belong to the instructor.
  3. Audit Localized Data: If window.academy_admin_ajax is not found, use browser_eval("window") to inspect all global variables for nonce keys or use grep -r "wp_localize_script" . to find the correct identifier.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.