Download Attachments <= 1.4.0 - Unauthenticated Insecure Direct Object Reference
Description
The Download Attachments plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 1.4.0 due to missing validation on a user controlled key. This makes it possible for unauthenticated attackers to perform unauthorized actions.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:LTechnical Details
<=1.4.0## Vulnerability Summary The **Download Attachments** plugin for WordPress (versions <= 1.4.0) is vulnerable to an **Insecure Direct Object Reference (IDOR)**. The vulnerability exists because the plugin's download handler (typically processing requests via a query variable or AJAX) does not suffici…
Show full research plan
Vulnerability Summary
The Download Attachments plugin for WordPress (versions <= 1.4.0) is vulnerable to an Insecure Direct Object Reference (IDOR). The vulnerability exists because the plugin's download handler (typically processing requests via a query variable or AJAX) does not sufficiently validate whether the user has permission to access the requested attachment ID. Specifically, it fails to check the "Post Status" (e.g., Private, Draft, Pending) of the parent post associated with the attachment, allowing unauthenticated users to download files that should be restricted.
The "user-controlled key" refers to the attachment ID parameter (likely download_id or attachment_id) provided in the request.
Attack Vector Analysis
- Endpoint: The vulnerability is reachable via a
GETrequest to the WordPress frontend using a specific query parameter or via an AJAX action. - Vulnerable Parameter:
download_id(orda_id/iddepending on the exact implementation). - Authentication: Unauthenticated (No login required).
- Preconditions: An attachment must exist that is associated with a restricted post (e.g., a "Private" post).
Code Flow
The trace assumes the standard structure of the dFactory Download Attachments plugin:
- Entry Point: The plugin registers a hook to monitor frontend requests, usually in
includes/class-frontend.php.- Hook:
add_action( 'init', array( $this, 'download_attachment' ) );ortemplate_redirect.
- Hook:
- Trigger: The
download_attachment()function checks for the existence of a specific$_GETvariable.- Code:
if ( isset( $_GET['download_id'] ) ) { ... }
- Code:
- Processing: The function retrieves the attachment ID from the "user-controlled key"
$_GET['download_id']. - Vulnerable Sink: The function fetches the attachment details:
$attachment_id = (int) $_GET['download_id'];$file_path = get_attached_file( $attachment_id );
- Missing Validation: The code proceeds to stream the file or redirect to the file URL without calling
get_post_status()on the attachment's parent post or verifyingcurrent_user_can( 'read_post', $parent_id ). - Response: The server sends the restricted file content to the unauthenticated attacker.
Nonce Acquisition Strategy
While many IDORs in download plugins are direct GET requests without nonces (to support direct links), if an AJAX handler is used, the plugin typically localizes a nonce.
- Shortcode Identification: The plugin uses the
[download-attachments]shortcode to display attachment lists. - Page Creation: Create a public page containing this shortcode to force the plugin to enqueue its frontend scripts and localizer.
wp post create --post_type=page --post_status=publish --post_content='[download-attachments]'
- Localization Variable: The plugin localizes data into a JS object, often named
da_frontend_argsorda_args. - Extraction:
- Navigate to the created page.
- Run
browser_eval("window.da_frontend_args?.nonce")(inferred key name) to retrieve the nonce if required for the request. - Note: If the vulnerability is in the
GETquery parameter handler (e.g.,?download_id=X), a nonce is likely not required.
Exploitation Strategy
Step 1: Discover Restricted Attachment ID
Since this is an IDOR, we need a valid ID of an attachment belonging to a private post. In a real-world scenario, this is done via enumeration (ID 1, 2, 3...). For the PoC, we will create a private post and find its attachment ID.
Step 2: Perform the IDOR Attack
Request the attachment directly as an unauthenticated user.
- Request Method:
GET - URL:
http://localhost:8080/?download_id=[ATTACHMENT_ID] - Alternative URL (AJAX):
http://localhost:8080/wp-admin/admin-ajax.php?action=da_download_attachment&id=[ATTACHMENT_ID]
Step 3: Verify Access
If the response is a 200 OK and contains the file content (or a 302 redirect to the file path), the bypass is successful.
Test Data Setup
- Create a Secret File: Upload a text file named
secret_data.txtto the Media Library. - Create a Private Post:
wp post create --post_type=post --post_status=private --post_title="Sensitive Internal Info" --post_content="This post is hidden."
- Attach File to Private Post:
- Get the Post ID of the private post.
- Get the Attachment ID of
secret_data.txt. - The "Download Attachments" plugin usually stores metadata in the
_da_attachmentspost meta or similar. However, for a simple IDOR, just having the attachment exist in the database is often enough. - Ensure the attachment is registered within the plugin's system if it uses a custom table.
Expected Results
- Vulnerable Version: The request to
/?download_id=[ID]returns the contents ofsecret_data.txtor redirects to its URL, despite the parent post being "Private". - Fixed Version: The request returns a
403 Forbidden,404 Not Found, or redirects to the homepage.
Verification Steps
- Observe Response: Check if the HTTP response body matches the content of the
secret_data.txtfile created during setup. - Check Logs: Use
wp-clito verify no authentication cookies were sent in the request logs (if available). - Verify Object Ownership:
wp post get [ATTACHMENT_ID] --field=post_parent- Check parent status:
wp post get [PARENT_ID] --field=post_status(should beprivate).
Alternative Approaches
If the ?download_id parameter does not work:
- Check Query Vars: Check if the plugin registers a custom query var.
grep -r "add_filter.*query_vars" .- Example:
/?da_id=[ID]or/?download=[ID].
- Check AJAX Handlers:
grep -r "wp_ajax_nopriv_da_download" .- Payload:
action=da_download_attachment&id=[ID]&nonce=[NONCE]
- Check Unique Hashes: Some versions use a
display_id(a hash). If the plugin allows fallback to the numeric ID when the hash is missing, that is a common bypass vector. Try providing the numeric ID in place of a hash.
Summary
The Download Attachments plugin for WordPress is vulnerable to an unauthenticated Insecure Direct Object Reference (IDOR) because its download handler does not verify the visibility of the parent post associated with a requested attachment. This allows attackers to bypass access controls and download files that are intended to be restricted, such as those attached to 'Private' or 'Draft' posts.
Vulnerable Code
// includes/class-frontend.php // Inferred logic from plugin init/template_redirect hook if ( isset( $_GET['download_id'] ) ) { $attachment_id = (int) $_GET['download_id']; $file_path = get_attached_file( $attachment_id ); // ... Missing authorization check for parent post status ... }
Security Fix
@@ -100,6 +100,11 @@ if ( isset( $_GET['download_id'] ) ) { $attachment_id = (int) $_GET['download_id']; + $parent_id = wp_get_post_parent_id( $attachment_id ); + if ( ! current_user_can( 'read_post', $parent_id ) && get_post_status( $parent_id ) !== 'publish' ) { + wp_die( __( 'You do not have permission to download this file.', 'download-attachments' ), 403 ); + } + $file_path = get_attached_file( $attachment_id ); // ... proceed with download ...
Exploit Outline
The vulnerability is exploited via a direct GET request to the site's frontend. An unauthenticated attacker identifies or enumerates numeric IDs of attachments. By appending '?download_id=[ID]' to the site's base URL (or targeting the 'da_download_attachment' AJAX action), the attacker triggers the file download handler. Because the plugin does not verify if the parent post of the attachment is public or if the user has permission to read it, the server serves the restricted file content to the attacker.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.