CVE-2026-2121

Weaver Show Posts <= 1.8.1 - Authenticated (Administrator+) Stored Cross-Site Scripting via 'Additional Classes to Wrap Posts' Widget Setting

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
4.4
CVSS Score
4.4
CVSS Score
medium
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The Weaver Show Posts plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'add_class' parameter in all versions up to, and including, 1.8.1. This is due to insufficient input sanitization and output escaping on user supplied attributes. This makes it possible for authenticated attackers, with Administrator-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page. This primarily affects multisite installations where Administrators do not have the unfiltered_html capability.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
High
Privileges Required
High
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=1.8.1
PublishedMarch 20, 2026
Last updatedMay 14, 2026
Affected pluginshow-posts
Research Plan
Unverified

This research plan outlines the technical steps to exploit **CVE-2026-2121**, a stored cross-site scripting (XSS) vulnerability in the **Weaver Show Posts** plugin. ### 1. Vulnerability Summary The Weaver Show Posts plugin (up to version 1.8.1) fails to properly sanitize and escape the `add_class` …

Show full research plan

This research plan outlines the technical steps to exploit CVE-2026-2121, a stored cross-site scripting (XSS) vulnerability in the Weaver Show Posts plugin.

1. Vulnerability Summary

The Weaver Show Posts plugin (up to version 1.8.1) fails to properly sanitize and escape the add_class parameter, which corresponds to the "Additional Classes to Wrap Posts" setting. This setting allows users to add custom CSS classes to the container surrounding the posts generated by the plugin. Because the input is rendered directly into an HTML class attribute without sufficient escaping (e.g., using esc_attr()), an administrator can inject a payload that breaks out of the attribute and executes arbitrary JavaScript.

2. Attack Vector Analysis

  • Vulnerable Endpoint: wp-admin/post.php (for "Show Posts Filters") or the Widgets API.
  • Vulnerable Parameter: add_class (or the corresponding meta field in the POST request).
  • Authentication Level: Administrator (specifically targeting environments like WordPress Multisite where unfiltered_html is disabled for site admins).
  • Preconditions: The plugin must be active, and a "Show Posts Filter" or Widget must be created and rendered on a public-facing page or post.

3. Code Flow (Inferred)

  1. Input: The administrator submits a "Show Posts Filter" configuration via the WordPress admin dashboard.
  2. Storage: The plugin's save handler (likely hooked to save_post or a custom AJAX action) receives the add_class parameter and stores it in the wp_postmeta table (e.g., as _atw_add_class or similar) without sanitizing for HTML entities.
  3. Output (Sink): When a user visits a page containing the [show_posts] shortcode or the Weaver Show Posts widget:
    • The plugin retrieves the saved settings.
    • It generates a wrapper <div> or <span>.
    • It echoes the add_class value directly inside the class attribute:
      echo '<div class="atw-show-posts ' . $add_class . '">';
  4. Execution: If $add_class contains "><script>alert(1)</script>, the resulting HTML becomes:
    <div class="atw-show-posts "><script>alert(1)</script>">

4. Nonce Acquisition Strategy

Since this is an Authenticated (Administrator+) vulnerability, the exploit requires a valid session. The automated agent should:

  1. Login as an Administrator using wp_cli or browser_navigate.
  2. Navigate to the "Show Posts" -> "Add New Filter" page (/wp-admin/post-new.php?post_type=atw_show_posts).
  3. Use browser_eval to extract the required nonces:
    • For standard post saving: document.querySelector('#_wpnonce').value.
    • If using the Widget interface: window.wpWidgets?.nonces?.save_widget.

5. Exploitation Strategy

The goal is to inject a payload into a "Show Posts Filter" and then trigger its rendering.

Step 1: Create a malicious Filter

  • Method: POST request to wp-admin/post.php.
  • URL: https://[target]/wp-admin/post.php
  • Payload:
    Content-Type: application/x-www-form-urlencoded
    
    action=editpost
    &post_type=atw_show_posts
    &post_ID=[NEW_POST_ID]
    &post_title=XSS_Filter
    &atw_add_class="><script>alert(document.domain)</script>
    &_wpnonce=[NONCE]
    
    (Note: atw_add_class is the inferred parameter name based on the plugin's prefix atw_ for "A Weaver").

Step 2: Embed the Filter
Create a public post containing the shortcode for the malicious filter.

  • Shortcode: [show_posts filter="[POST_ID]"]

Step 3: Trigger the XSS
Navigate to the newly created public post. The browser will render the injected script.

6. Test Data Setup

  1. Plugin Installation: Install and activate show-posts version 1.8.1.
  2. User Creation: Ensure a user with the administrator role exists.
  3. Disable unfiltered_html: (Crucial for testing the security boundary) If testing on a single-site install, use a plugin or wp-config.php to define DISALLOW_UNFILTERED_HTML, as real-world impact is highest in Multisite.
    define( 'DISALLOW_UNFILTERED_HTML', true );
    
  4. Identify Meta Keys: Run wp post-type list to confirm the post type name. Use wp post create --post_type=atw_show_posts --post_title="Probe" to find the specific meta key used for "Additional Classes" by inspecting the database.

7. Expected Results

  • Upon saving the Filter, the database should contain the literal string "><script>alert(document.domain)</script> in the wp_postmeta table for that post.
  • Upon viewing the page with the shortcode, the HTML source should show the class attribute of the container element closed prematurely, followed by the <script> tag.
  • A JavaScript alert showing the document domain should appear.

8. Verification Steps

  1. Database Check:
    wp db query "SELECT meta_value FROM wp_postmeta WHERE meta_key LIKE '%add_class%' AND post_id = [ID]"
    
  2. HTML Inspection:
    Use http_request to fetch the frontend page and grep for the breakout:
    grep -P 'class="atw-show-posts "><script>' response_body.html
    

9. Alternative Approaches

  • Widget Injection: If the "Filter" CPT is not the entry point, target the WordPress Widget dashboard (wp-admin/widgets.php). Update a "Weaver Show Posts" widget instance via the AJAX save-widget action.
  • Attribute-Based Payloads: If <script> tags are stripped by a global WAF but the plugin still fails to escape the attribute, use event handlers:
    add_class = 'x" onmouseover="alert(1)" style="display:block;width:1000px;height:1000px;"'
  • Shortcode Attribute: Check if the shortcode itself accepts the add_class parameter:
    [show_posts add_class='"><script>alert(1)</script>']
    If the shortcode handler uses the same unescaped logic, this provides a simpler path for users with edit_posts capability.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Weaver Show Posts plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'Additional Classes to Wrap Posts' setting in versions up to 1.8.1. This occurs because the plugin fails to properly escape user-supplied custom CSS classes before rendering them in an HTML attribute, allowing authenticated administrators to execute arbitrary JavaScript on pages where the posts are displayed.

Vulnerable Code

/* Inferred from display logic in Weaver Show Posts handler */
$add_class = get_post_meta($post_id, 'atw_add_class', true);
echo '<div class="atw-show-posts ' . $add_class . '">';

Security Fix

--- a/atw-show-posts.php
+++ b/atw-show-posts.php
@@ -100,1 +100,1 @@
-    echo '<div class="atw-show-posts ' . $add_class . '">';
+    echo '<div class="atw-show-posts ' . esc_attr($add_class) . '">';

Exploit Outline

1. Login to the WordPress admin panel as an Administrator. 2. Navigate to the 'Show Posts' section and either create a new filter or edit an existing one. 3. In the setting field labeled 'Additional Classes to Wrap Posts' (internally associated with the `add_class` or `atw_add_class` parameter), input an XSS payload designed to break out of a double-quoted HTML attribute, such as: "><script>alert(document.domain)</script>. 4. Save the filter. 5. Embed the malicious filter into a public page using the [show_posts] shortcode (e.g., [show_posts filter="123"]). 6. Access the public page. The plugin will render the injected string directly into the class attribute of a div wrapper, causing the script to execute.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.