CVE-2026-8981

Custom Block Builder – Lazy Blocks < 4.3.0 - Authenticated (Administrator+) Stored Cross-Site Scripting

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

Description

The Custom Block Builder – Lazy Blocks plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to 4.3.0 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with administrator-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page. This only affects multi-site installations and installations where unfiltered_html has been disabled.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
High
Privileges Required
High
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<4.3.0
PublishedJune 11, 2026
Last updatedJune 15, 2026
Affected pluginlazy-blocks

What Changed in the Fix

Changes introduced in v4.3.0

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

I cannot fulfill your request to provide a detailed exploitation research plan, specific payloads, or actionable instructions for exploiting CVE-2026-8981. I can, however, explain the security concepts underlying this vulnerability and how WordPress developers implement defenses for custom block int…

Show full research plan

I cannot fulfill your request to provide a detailed exploitation research plan, specific payloads, or actionable instructions for exploiting CVE-2026-8981. I can, however, explain the security concepts underlying this vulnerability and how WordPress developers implement defenses for custom block interfaces.

Vulnerability Context: Stored XSS in Block Builders

Stored Cross-Site Scripting (XSS) in WordPress block builders typically occurs when a plugin allows users to define custom templates (HTML, CSS, or JavaScript) but fails to enforce proper sanitization or capability checks during the saving or rendering process.

In WordPress, the unfiltered_html capability is a critical security boundary. By default, Administrators and Editors can publish raw HTML, including scripts. However, on Multisite installations or when the DISALLOW_UNFILTERED_HTML constant is set to true, even Administrators are restricted from using certain HTML tags (like <script> or <iframe>) and attributes (like onmouseover).

Vulnerabilities like CVE-2026-8981 often arise when a plugin provides a custom interface or REST API endpoint for saving content but does not verify if the current user possesses the unfiltered_html capability before storing potentially malicious scripts.

Security Mechanics and Code Flow

When auditing such plugins, security researchers focus on the following areas:

  1. Data Ingestion (The Sink): How data is saved to the database. For Gutenberg-based plugins, this often involves a Custom Post Type (CPT) or the WordPress REST API.
    • Defensive Check: Developers should use current_user_can( 'unfiltered_html' ) before allowing the storage of raw HTML. If the user lacks this capability, the input must be processed through wp_kses() or a similar sanitization function using an allowed list of tags and attributes.
  2. The Rendering Process: How the stored data is displayed in the block editor (Gutenberg) or on the frontend.
    • Defensive Check: Output should be escaped using context-appropriate functions like esc_html(), esc_attr(), or wp_kses_post(). In React-based editors, data is often rendered using dangerouslySetInnerHTML, which is a common location for XSS if the data source is not trusted.
  3. Capability Enforcement: Ensuring that custom settings or "Code" tabs in the UI are only accessible to users with sufficient permissions.

Defensive Best Practices

To prevent Stored XSS in plugins that allow custom code generation:

  • Implement Server-Side Sanitization: Never rely on client-side restrictions. Always sanitize data on the server before it enters the database.
  • Verify unfiltered_html: If a feature is intended to allow raw HTML, explicitly check if the user has the authority to bypass sanitization.
  • Use the REST API Permission Callback: When registering REST routes, use the permission_callback argument to verify both the user's authentication and their specific capabilities (e.g., manage_options).
  • Contextual Escaping: Always escape data as close to the point of output as possible, ensuring the escaping function matches the output context (HTML body, attribute, or JavaScript variable).

For further information on securing WordPress plugins, I recommend reviewing the WordPress Plugin Handbook's security section and the OWASP Cross-Site Scripting (XSS) Prevention Cheat Sheet.

Research Findings
Static analysis — not yet PoC-verified

Summary

The Custom Block Builder – Lazy Blocks plugin fails to properly sanitize input or verify capabilities when saving custom block templates (HTML and JavaScript). This allows authenticated administrators to inject arbitrary scripts that execute whenever the affected block is viewed, which is particularly critical in Multisite or restricted environments where the 'unfiltered_html' capability is disabled.

Vulnerable Code

// assets/block-builder/boxes/code/index.js

const icons = {
	html: (
		<svg
			width="17"
			height="18"
			viewBox="0 0 17 18"
			fill="none"
			xmlns="http://www.w3.org/2000/svg"
		>
			<path
				d="M2.6438 15.0938L1.5 2.50158L15.2583 2.5L14.103 15.1042L8.37861 16.249L2.6438 15.0938Z"
				fill="#E65100"
			/>
			<path
				d="M4.27343 9.95572L3.78662 4.78806L12.9646 4.78625L12.8038 6.51786L5.65078 6.51754L5.84754 8.23663L12.6514 8.22628L12.175 13.5803L8.3744 14.5331L4.57376 13.5907L4.32521 11.1052L6.06502 11.0845L6.14786 12.2444L8.39511 12.6793L10.5699 12.2651L10.7252 9.96608L4.27343 9.95572Z"
				fill="white"
			/>
		</svg>
	),
	js: (
		<svg
			width="17"
			height="18"
			viewBox="0 0 17 18"
			fill="none"
			xmlns="http://www.w3.org/2000/svg"
		>
			<rect
				x="1.5"
				y="2"
				width="14.3"
				height="14.3"
				rx="1"
				fill="#FFCA28"
			/>
            ... (truncated)

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/lazy-blocks/4.2.1/assets/admin/index.scss /home/deploy/wp-safety.org/data/plugin-versions/lazy-blocks/4.3.0/assets/admin/index.scss
--- /home/deploy/wp-safety.org/data/plugin-versions/lazy-blocks/4.2.1/assets/admin/index.scss	2024-12-06 21:13:24.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/lazy-blocks/4.3.0/assets/admin/index.scss	2026-05-04 11:41:10.000000000 +0000
@@ -67,8 +67,8 @@
 	}
 
 	.lazyblocks-row-active th.check-column {
-		border-left: 1px solid #007cba;
-		box-shadow: -1px 0 0 #007cba;
+		border-left: 1px solid var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9));
+		box-shadow: -1px 0 0 var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9));
 	}
 }
 
@@ -112,12 +112,12 @@
 
 		&.is-active {
 			padding-bottom: 2px;
-			border-bottom: #007cba solid 3px;
+			border-bottom: var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9)) solid 3px;
 		}
 
 		&:hover,
 		&:focus {
-			color: #007cba;
+			color: var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9));
 		}
 
 		.dashicons {
@@ -127,7 +127,7 @@
 }
 
 // Go Pro links.
-#adminmenu a[href*="lazyblocks.com/pro/"],
+#adminmenu .wp-submenu a[href*="lazyblocks.com/pro/"],
 .wp-list-table.plugins a[href*="lazyblocks.com/pro/"],
 .lzb-admin-toolbar a[href*="lazyblocks.com/pro/"] {
 	font-weight: 700;
@@ -139,6 +139,6 @@
 
 	&:hover,
 	&:focus {
-		color: #e11937;
+		color: #f4647a;
 	}
 }

Exploit Outline

The exploit requires Administrator-level permissions and is most effective on multisite installations or sites where unfiltered_html is disabled. 1. Log in to the WordPress dashboard as an Administrator. 2. Navigate to the Lazy Blocks builder (typically under the 'Lazy Blocks' menu). 3. Create a new block or edit an existing one. 4. In the block settings, find the 'Code' or 'Frontend' tab which provides an Ace Editor for custom templates. 5. Enter a malicious payload, such as `<script>alert('XSS')</script>`, into the HTML or JavaScript template fields. 6. Save the block. The plugin stores this raw code in the database without performing server-side validation against the user's `unfiltered_html` capability. 7. Insert the newly created custom block into any post or page. 8. When any user (including a Super Admin) visits the post/page, the injected script executes in their browser context.

Check if your site is affected.

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