CVE-2026-0894

Content Blocks (Custom Post Widget) <= 3.3.9 - Authenticated (Author+) Stored Cross-Site Scripting via content_block Shortcode

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

Description

The Content Blocks (Custom Post Widget) plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the plugin's content_block shortcode in all versions up to, and including, 3.3.9 due to insufficient input sanitization and output escaping on user supplied values consumed from user-created content blocks. 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.3.9
PublishedApril 17, 2026
Last updatedApril 18, 2026
Affected plugincustom-post-widget

What Changed in the Fix

Changes introduced in v3.4.1

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

look at `custom-post-widget.php` line 154 again. * `$content = apply_filters( 'the_content', $post->post_content );` * This is the code for `cpw_content_block` shortcode. * If the vulnerability is in the *other* shortcode `content_block` (defined in `shortcode.php`), and that one …

Show full research plan

look at custom-post-widget.php line 154 again.
* $content = apply_filters( 'the_content', $post->post_content );
* This is the code for cpw_content_block shortcode.
* If the vulnerability is in the other shortcode content_block (defined in shortcode.php), and that one doesn't use apply_filters.

*   Let's assume the `title_tag` injection is the primary vector because it allows an Author to use a perfectly safe Post Title as the body of an injected `<script>` tag.

1.  Vulnerability Summary.
2.  Attack Vector Analysis (Author+).
3.  Code Flow (Inferred `shortcode.php`).
4.  Nonce Strategy (None for shortcode usage).
5.  Exploitation Strategy (Step-by-step).
6.  Test Data Setup.
7.  Expected Results.
8.  Verification.

I will use `title_tag` as the primary exploit candidate and `class` as the alternative.

One more check: "insufficient input sanitization and output escaping on user supplied values **consumed from user-created content blocks**."
This wording is very specific.
If the Author creates a block and fills in the `_content_block_information` field.
If the short
Research Findings
Static analysis — not yet PoC-verified

Summary

The Content Blocks plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the `content_block` shortcode. An authenticated attacker can manipulate the `markup` attribute to inject unauthorized HTML tags such as `<script>`, which causes the content of a linked content block to be executed as JavaScript in the victim's browser.

Vulnerable Code

// shortcode.php line 31
$markup = sanitize_text_field ( $params['markup'] );

---

// shortcode.php line 69
$content = '<' . $markup . ' class="' . $class . '" id="custom_post_widget-' . $id . '">';
if ( $show_post_title == 'yes' ) {
    $content .= '<' . $title_tag . ' class="custom_post_widget_title">' . $post -> post_title . '</' . $title_tag . '>';
}
$content .= apply_filters( 'the_content', $post -> post_content );

// ...

$content .= '</' . $markup . '>';

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/custom-post-widget/3.3.9/shortcode.php /home/deploy/wp-safety.org/data/plugin-versions/custom-post-widget/3.4.1/shortcode.php
--- /home/deploy/wp-safety.org/data/plugin-versions/custom-post-widget/3.3.9/shortcode.php	2024-09-06 11:09:32.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/custom-post-widget/3.4.1/shortcode.php	2026-01-27 13:29:16.000000000 +0000
@@ -1,5 +1,7 @@
 <?php
 
+if ( ! defined( 'ABSPATH' ) ) exit;
+
 // Add the ability to display the content block in a reqular post using a shortcode
 function custom_post_widget_shortcode ( $atts ) {
 	$params = shortcode_atts ( array (
@@ -28,7 +30,11 @@
 	if ( ! in_array( $title_tag, $allowed_tags ) ) {
 		$title_tag = 'h3'; // Default to 'h3' if the specified tag is not allowed
 	}
-	$markup = sanitize_text_field ( $params['markup'] );
+	$markup = strtolower( sanitize_text_field( $params['markup'] ) );
+	$allowed_markup_tags = array( 'div', 'section', 'article', 'aside', 'header', 'footer', 'main', 'span', 'p' );
+	if ( ! in_array( $markup, $allowed_markup_tags, true ) ) {
+		$markup = 'div';
+	}
 	$template = sanitize_text_field ( $params['template'] );
 
 	if ( $slug ) {

Exploit Outline

1. Login as an authenticated user with Contributor-level access or higher. 2. Create a new 'Content Block' post (post_type: `content_block`). Set the content of this block to the JavaScript payload (e.g., `alert(origin)`). Note the post ID of this block. 3. Create or edit a standard post or page. 4. Insert the shortcode `[content_block id=YOUR_BLOCK_ID markup="script"]` into the editor. 5. Publish or preview the post. 6. The plugin's shortcode handler will output the block content wrapped in `<script>` tags instead of the intended `<div>` tags, causing the browser to execute the content as JavaScript.

Check if your site is affected.

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