Blog Filter <= 1.7.6 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The Blog Filter plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.7.6 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
What Changed in the Fix
Changes introduced in v1.7.7
Source Code
WordPress.org SVNThis research plan focuses on exploiting a Stored Cross-Site Scripting (XSS) vulnerability in the **Blog Filter** plugin (<= 1.7.6). The vulnerability stems from the plugin's failure to sanitize and escape shortcode attributes, specifically the `default_filter_term` parameter, when rendering JavaScr…
Show full research plan
This research plan focuses on exploiting a Stored Cross-Site Scripting (XSS) vulnerability in the Blog Filter plugin (<= 1.7.6). The vulnerability stems from the plugin's failure to sanitize and escape shortcode attributes, specifically the default_filter_term parameter, when rendering JavaScript initialization code in the frontend.
1. Vulnerability Summary
The Blog Filter plugin allows users to display filtered blog posts via the [AWL-BlogFilter] shortcode. In versions up to 1.7.6, the attribute default_filter_term (and potentially others) is extracted from the shortcode and echoed directly into a <script> block within the filtering/filters-ajax.php file without any escaping. Because authenticated users with the Contributor role can create posts and include shortcodes, they can inject malicious JavaScript into pages viewed by other users, including administrators.
2. Attack Vector Analysis
- Shortcode:
[AWL-BlogFilter] - Vulnerable Attribute:
default_filter_term - Authentication Level: Authenticated (Contributor+)
- Vulnerable Sink: Inline JavaScript in
filtering/filters-ajax.php - Injection Type: Breaking out of a JavaScript string literal.
3. Code Flow
- Entry Point: A user with Contributor+ privileges creates or edits a post containing the shortcode:
[AWL-BlogFilter default_filter_term="PAYLOAD"]. - Processing: In
blog-filter-shortcode.php, theawl_blog_filter_shortcode()function is executed:- Line 52:
$atts = shortcode_atts($defaults, $user_atts, 'AWL-BlogFilter');populates$attswith user-defined attributes. - Line 93:
$default_filter = $atts['default_filter_term'];assigns the raw attribute value to a variable. - Line 99:
extract($atts);makes$default_filter_termavailable in the local scope.
- Line 52:
- Rendering: The shortcode function eventually includes template files (likely
filtering/filters-ajax.php) to render the filtering logic. - Sink: In
filtering/filters-ajax.php:- Line 101 (approx):
options<?php echo $unique_id; ?> = { ... filter: '<?php echo $default_filter; ?>', ... } - The value of
$default_filteris echoed directly into the JavaScriptfilterproperty. By using a single quote and a semicolon, an attacker can terminate the assignment and inject arbitrary code.
- Line 101 (approx):
4. Nonce Acquisition Strategy
This vulnerability is triggered during the rendering of a shortcode on the frontend. No WordPress nonce is required to trigger the XSS when viewing a page.
However, if the exploitation agent needs to verify the state of the plugin via AJAX or update settings, it would follow this strategy:
- Identify Variable: The plugin uses
wp_localize_scriptfor AJAX configuration (visible inblog-filter.php). - JS Variable: Usually localized as a global object (e.g.,
bfg_admin_objor similar, inferred from standard WP patterns inblog-filter.phpAJAX callbacks). - Extraction:
browser_navigateto/wp-admin/edit.php?page=blog-filter-settings-page.browser_eval("window.bfg_admin_nonce")(or the specific key found in the localized script).
5. Exploitation Strategy
The goal is to inject a payload that executes alert(origin) to demonstrate XSS.
Payload Construction:
Since the sink is filter: 'PAYLOAD',, we use:'); alert(origin); //
Resulting JS:
filter: ''); alert(origin); //',
Step-by-Step Execution:
- Create Malicious Post: Use the
http_requesttool to create a post as a Contributor.- URL:
http://localhost:8080/wp-admin/post-new.php - Method:
POST - Body: Standard WordPress post creation parameters, including:
post_title:XSS Testcontent:[AWL-BlogFilter default_filter_term="');alert(origin);('"]status:publish(Note: Contributors may only be able to save aspending, requiring an admin to view the preview or publish it).
- URL:
- Navigate to Post: As an administrator (to simulate the high-value target), navigate to the newly created post URL using
browser_navigate. - Observation: Use
browser_wait_for_selectoror check for the alert.
6. Test Data Setup
- User: Create a user with the
contributorrole. - Plugin: Ensure
blog-filterversion 1.7.6 is installed and active. - Content: The contributor must have a post where they can place the shortcode.
7. Expected Results
- When the post is rendered, the HTML source will contain the injected JavaScript.
- The browser will execute
alert(origin). - The browser's console will not show syntax errors that prevent execution (ensured by the
('tail in the payload).
8. Verification Steps
- Source Check: Navigate to the post and inspect the source for:
filter: '');alert(origin);('', - Database Check: Use WP-CLI to verify the payload is stored:
wp post get [ID] --field=post_content - Behavioral Check: Use Playwright's
page.on('dialog', ...)to programmatically catch the alert.
9. Alternative Approaches
If default_filter_term is sanitized in some environments (though unlikely given the code), check other attributes extracted in blog-filter-shortcode.php that are used in filtering/filters-ajax.php:
blog_template: Used infiltering/filters-ajax.phpline 34.blog_fixed_grid: Used in multipleifconditions within the JS blocks.blog_on_load_scroll: Used as a data attribute inblog-filter-shortcode.phpline 111. If not escaped, this provides an attribute-based XSS vector:"><script>alert(1)</script>.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.