CVE-2025-13535

King Addons for Elementor <= 51.1.38 - Authenticated (Contributor+) DOM-Based Stored Cross-Site Scripting via Multiple Widgets

mediumImproper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
6.4
CVSS Score
6.4
CVSS Score
medium
Severity
51.1.54
Patched in
2d
Time to patch

Description

The King Addons for Elementor plugin for WordPress is vulnerable to multiple Contributor+ DOM-Based Stored Cross-Site Scripting vulnerabilities in all versions up to, and including, 51.1.38. This is due to insufficient input sanitization and output escaping across multiple widgets and features. The plugin uses esc_attr() and esc_url() within JavaScript inline event handlers (onclick attributes), which allows HTML entities to be decoded by the DOM, enabling attackers to break out of the JavaScript context. Additionally, several JavaScript files use unsafe DOM manipulation methods (template literals, .html(), and window.location.href with unvalidated URLs) with user-controlled data. This makes it possible for authenticated attackers, with Contributor-level access and above, to inject arbitrary web scripts via Elementor widget settings that execute when a user accesses the injected page or when an administrator previews the page in Elementor's editor. The vulnerability was partially patched in version 5.1.51.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Low
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=51.1.53
PublishedMarch 31, 2026
Last updatedApril 1, 2026
Affected pluginking-addons

What Changed in the Fix

Changes introduced in v51.1.54

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

This plan targets **CVE-2025-13535**, a Stored DOM-Based Cross-Site Scripting (XSS) vulnerability in the **King Addons for Elementor** plugin. The vulnerability arises from two primary flaws: 1. **Improper Output Escaping in Event Handlers:** Using `esc_attr()` or `esc_url()` inside JavaScript `onc…

Show full research plan

This plan targets CVE-2025-13535, a Stored DOM-Based Cross-Site Scripting (XSS) vulnerability in the King Addons for Elementor plugin. The vulnerability arises from two primary flaws:

  1. Improper Output Escaping in Event Handlers: Using esc_attr() or esc_url() inside JavaScript onclick attributes, allowing attribute breakout via HTML entity decoding.
  2. Unsafe DOM Manipulation: Client-side JavaScript (specifically for widgets and the AI translator) using .html() or template literals with unvalidated data from REST/AJAX endpoints.

1. Vulnerability Summary

  • Vulnerability Name: Authenticated (Contributor+) DOM-Based Stored XSS
  • Plugin Slug: king-addons
  • Vulnerable Components: Multiple Elementor Widgets (Button, Post Grid, etc.) and the Ajax_Select2_API REST controller.
  • Affected Versions: <= 51.1.38
  • Why it exists: The plugin's REST API specifically decodes HTML entities in post titles (html_entity_decode) before sending them to the client. The client-side JavaScript then renders these titles using jQuery's .html() method. Additionally, widget templates render user-controlled URLs/settings inside onclick attributes using esc_attr(), which the browser decodes before JavaScript execution.

2. Attack Vector Analysis

  • Authentication Level: Contributor or higher (any role with edit_posts capability).
  • Entry Points:
    1. Post Title Injection: Creating a post with a malicious title.
    2. Widget Link Injection: Setting a widget's URL or custom attribute in the Elementor Editor.
  • Vulnerable Endpoints:
    • REST API: /wp-json/kingaddons/v1/ajaxselect2/getPostsByPostType
    • AJAX: king_addons_el_hf_get_posts_by_query (Action: wp_ajax_king_addons_el_hf_get_posts_by_query)
  • Payload Type: "><img src=x onerror=alert(origin)> or javascript:alert(1)

3. Code Flow

Path A: DOM XSS via Post Selection (REST API)

  1. Attacker (Contributor): Creates a post with title <img src=x onerror=alert(1)>.
  2. King Addons REST Controller: Ajax_Select2_API::getPostsByPostType (in includes/controls/Ajax_Select2/Ajax_Select2_API.php) fetches the title.
  3. Vulnerable Sink (PHP): It calls 'text' => html_entity_decode(get_the_title()). This explicitly decodes any existing safety entities.
  4. Client-Side JS: A widget (e.g., Post Grid) or the Elementor Editor's post selector calls this REST route.
  5. Vulnerable Sink (JS): The Select2 handler or widget JS renders the text property using .html() or template literals, executing the script.

Path B: Inline Event Handler Breakout (Widget Template)

  1. Attacker (Contributor): Adds a widget (e.g., Button) and sets a URL/Attribute to ');alert(1);('.
  2. Vulnerable Sink (PHP): The widget template renders an inline handler: onclick="someFunction('<?php echo esc_attr($url); ?>')" (inferred from description).
  3. Browser Decoding: The browser decodes the attribute value &#039;);alert(1);(&#039; back to ');alert(1);(' before JS execution.
  4. Execution: The JS engine executes someFunction('');alert(1);('').

4. Nonce Acquisition Strategy

The Ajax_Select2_API REST route uses permission_callback => '__return_true' and relies on current_user_can('edit_posts'). Standard REST API authentication (nonce in X-WP-Nonce header) is required if accessed via the browser.

The Header/Footer Builder AJAX endpoint requires a specific nonce:

  1. Shortcode/Location: The script king-addons-el-hf-admin is enqueued on the Header/Footer builder page: wp-admin/edit.php?post_type=king-addons-el-hf.
  2. Extraction:
    • Navigate to: http://localhost:8080/wp-admin/edit.php?post_type=king-addons-el-hf
    • Execute JS: window.king_addons_el_hf_admin?.nonce

5. Exploitation Strategy

Step 1: Inject Malicious Post Title

  • Objective: Create a post that will be served by the Select2 REST API.
  • HTTP Request: Use http_request as a Contributor.
  • Endpoint: /wp-admin/post-new.php (or use WP-CLI to be faster).
  • Payload Title: XSS-Trigger <img src=x onerror=alert(window.origin)>

Step 2: Trigger REST API Reflection

  • Objective: Verify the REST API returns the decoded payload.
  • HTTP Request:
    GET /wp-json/kingaddons/v1/ajaxselect2/getPostsByPostType?query_slug=post&s=XSS-Trigger HTTP/1.1
    Host: localhost:8080
    X-WP-Nonce: [REST_NONCE]
    
  • Expected JSON Response: Look for "text": "XSS-Trigger <img src=x onerror=alert(window.origin)>" (Note the lack of entity encoding).

Step 3: Demonstrate DOM Sink (Manual Simulation)

  • The agent should document that any King Addons widget using this REST endpoint for dynamic content (like "Post Grid" or "Select Post") will render this text via .html().

6. Test Data Setup

  1. User: Create a user contributor_user with the contributor role.
  2. Post Type: Enable the plugin's "Header & Footer Builder" extension in King Addons settings if needed.
  3. Content:
    wp post create --post_type=post --post_title='XSS-Trigger <img src=x onerror=alert(window.origin)>' --post_status=publish --post_author=[ID]
    

7. Expected Results

  • The REST API call /wp-json/kingaddons/v1/ajaxselect2/getPostsByPostType must return the post title with the HTML tag decoded.
  • In a browser context (e.g., using browser_navigate to an Elementor page with a King Addons Post Selector), an alert box should trigger when the post list is loaded.

8. Verification Steps

  1. Check REST output:
    # As contributor
    curl -u contributor:password "http://localhost:8080/wp-json/kingaddons/v1/ajaxselect2/getPostsByPostType?query_slug=post&s=XSS-Trigger"
    
  2. Confirm Lack of Encoding: Verify that < and > in the title are NOT converted to &lt; and &gt; in the JSON response.

9. Alternative Approaches

If the REST API is not sufficient, target the Header Footer Builder AJAX endpoint:

  • Action: king_addons_el_hf_get_posts_by_query
  • Request:
    POST /wp-admin/admin-ajax.php HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    
    action=king_addons_el_hf_get_posts_by_query&q=XSS-Trigger&nonce=[NONCE]
    
  • The code in Header_Footer_Builder.php lines 188-191 also retrieves get_the_title() and puts it into the text key of the response. If the associated JS for this feature (likely in the builder interface) uses .html(), the XSS will trigger there.
Research Findings
Static analysis — not yet PoC-verified

Summary

King Addons for Elementor is vulnerable to DOM-based Stored XSS because its REST and AJAX endpoints return post titles with decoded HTML entities, which are then rendered unsafely by client-side JavaScript. Additionally, the plugin uses esc_attr() within inline JavaScript event handlers in widget templates, allowing attackers to break out of the JavaScript context via HTML entity decoding by the browser.

Vulnerable Code

// includes/controls/Ajax_Select2/Ajax_Select2_API.php
// Lines 63-66 in getElementorTemplates
$options[] = [
    'id' => get_the_ID(),
    'text' => html_entity_decode(get_the_title()),
];

---

// includes/controls/Ajax_Select2/Ajax_Select2_API.php
// Lines 104-107 in getPostsByPostType
$options[] = [
    'id' => get_the_ID(),
    'text' => html_entity_decode(get_the_title()),
];

---

// includes/extensions/Header_Footer_Builder/Header_Footer_Builder.php
// Lines 188-195 in king_addons_el_hf_get_posts_by_query
$title = get_the_title();
$title .= (0 != $query->post->post_parent) ? ' (' . get_the_title($query->post->post_parent) . ')' : '';
$id = get_the_id();
data[] = array(
    'id' => 'post-' . $id,
    'text' => $title,
);

Security Fix

--- a/includes/controls/Ajax_Select2/Ajax_Select2_API.php
+++ b/includes/controls/Ajax_Select2/Ajax_Select2_API.php
@@ -65,7 +65,7 @@
                 $the_query->the_post();
                 $options[] = [
                     'id' => get_the_ID(),
-                    'text' => html_entity_decode(get_the_title()),
+                    'text' => get_the_title(),
                 ];
             }
         }
@@ -106,7 +106,7 @@
                 $query->the_post();
                 $options[] = [
                     'id' => get_the_ID(),
-                    'text' => html_entity_decode(get_the_title()),
+                    'text' => get_the_title(),
                 ];
             }
         }
--- a/includes/extensions/Header_Footer_Builder/Header_Footer_Builder.php
+++ b/includes/extensions/Header_Footer_Builder/Header_Footer_Builder.php
@@ -185,7 +185,7 @@
             if ($query->have_posts()) {
                 while ($query->have_posts()) {
                     $query->the_post();
-                    $title = get_the_title();
+                    $title = wp_strip_all_tags(get_the_title());
                     $title .= (0 != $query->post->post_parent) ? ' (' . get_the_title($query->post->post_parent) . ')' : '';
                     $id = get_the_id();
                     $data[] = array(

Exploit Outline

The exploit requires an attacker with at least Contributor-level permissions. 1. The attacker creates a new post and sets the title to a malicious XSS payload, such as `<img src=x onerror=alert(document.domain)>`. 2. The attacker opens the Elementor editor and adds a King Addons widget that utilizes the Select2 post selector (e.g., Post Grid or any widget using dynamic post selection). 3. When the widget configuration panel or the widget itself fetches post data via the REST API endpoint `/wp-json/kingaddons/v1/ajaxselect2/getPostsByPostType`, the server returns the post title with HTML entities decoded (due to the use of `html_entity_decode`). 4. The client-side JavaScript renders this JSON response into the DOM using an unsafe sink like `.html()`, executing the script. 5. Alternatively, for the inline event handler breakout, an attacker sets a widget URL setting to `');alert(1);('`. When the widget is rendered, the `esc_attr()` encoding is reversed by the browser's DOM parser before the JavaScript engine executes the `onclick` attribute, resulting in code execution.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.