Happy Addons for Elementor <= 3.21.0 - Insecure Direct Object Reference to Authenticated (Contributor+) Stored Cross-Site Scripting via Template Conditions
Description
The Happy Addons for Elementor plugin for WordPress is vulnerable to Insecure Direct Object Reference in all versions up to, and including, 3.21.0 via the `ha_condition_update` AJAX action. This is due to the `validate_reqeust()` method using `current_user_can('edit_posts', $template_id)` instead of `current_user_can('edit_post', $template_id)` — failing to perform object-level authorization. Additionally, the `ha_get_current_condition` AJAX action lacks a capability check. This makes it possible for authenticated attackers, with Contributor-level access and above, to modify the display conditions of any published `ha_library` template. Because the `cond_to_html()` renderer outputs condition values into HTML attributes without proper escaping (using string concatenation instead of `esc_attr()`), an attacker can inject event handler attributes (e.g., `onmouseover`) that execute JavaScript when an administrator views the Template Conditions panel, resulting in Stored Cross-Site Scripting.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:L/A:NTechnical Details
<=3.21.0What Changed in the Fix
Changes introduced in v3.21.1
Source Code
WordPress.org SVN# Research Plan: CVE-2026-2918 - Happy Addons for Elementor IDOR to Stored XSS ## 1. Vulnerability Summary The **Happy Addons for Elementor** plugin (up to version 3.21.0) contains an Insecure Direct Object Reference (IDOR) vulnerability in the `ha_condition_update` AJAX action. The flaw exists in …
Show full research plan
Research Plan: CVE-2026-2918 - Happy Addons for Elementor IDOR to Stored XSS
1. Vulnerability Summary
The Happy Addons for Elementor plugin (up to version 3.21.0) contains an Insecure Direct Object Reference (IDOR) vulnerability in the ha_condition_update AJAX action. The flaw exists in the Condition_Manager::validate_reqeust() (sic) method, which uses current_user_can('edit_posts', $template_id) instead of the object-specific edit_post. Because "Contributor" roles possess the edit_posts capability, they can pass this check for any template ID.
Furthermore, the condition data stored via this action is rendered in the WordPress admin panel using the cond_to_html() method (inferred to be in classes/condition-manager.php) via string concatenation without proper escaping (e.g., esc_attr()). This allows an attacker to inject malicious HTML attributes (like onmouseover) into the template conditions, leading to Stored Cross-Site Scripting (XSS) that executes when an administrator views the conditions.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
ha_condition_update(for XSS injection) andha_cond_get_current(for info disclosure) - Vulnerable Parameter:
conditions(specifically the values within the nested array) andtemplate_id. - Authentication Level: Contributor+ (any user with
edit_postscapability). - Preconditions: At least one
ha_librarytemplate must exist (created by an admin).
3. Code Flow
- Entry Point: The AJAX request triggers
Condition_Manager::process_condition_update(). - Authorization Check: It calls
validate_reqeust($template_id). - The Flaw:
validate_reqeustcheckscurrent_user_can('edit_posts', $template_id). Sinceedit_postsis a general capability held by Contributors and the second argument$template_idis ignored by WordPress when checking the general capability stringedit_posts, the check returnstrueregardless of who owns the post. - Data Persistence: The malicious
conditionsarray is processed and saved to post meta (likely_ha_template_conditions). - XSS Sink: When an Admin opens the "Template Conditions" modal (managed by
Theme_Builderinclasses/theme-builder.php), the plugin callscond_to_html(). This method concatenates the condition values into HTML tags. - Execution: The injected event handler (e.g.,
onmouseover) executes in the Admin's browser context.
4. Nonce Acquisition Strategy
The AJAX actions require a nonce localized by the plugin.
- Location: The nonce is likely localized in the
HappyAddonsEditorobject within the Elementor editor or theha_librarylist page. - Strategy:
- Navigate to the
ha_librarylist page (/wp-admin/edit.php?post_type=ha_library) as a Contributor. - Use
browser_evalto extract the nonce. - JS Object Path:
window.HappyAddonsEditor?.nonceorwindow.HappyAddons?.nonce. - Note: If the nonce is only loaded in the Elementor editor (which Contributors might not be able to access for specific templates), look for the nonce in
admin-ajax.php?action=ha_cond_template_typeor similar helper actions.
- Navigate to the
5. Exploitation Strategy
Step 1: Enumerate Templates (IDOR Information Disclosure)
Verify you can read conditions of templates you don't own.
- Request:
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=ha_cond_get_current&template_id=[TARGET_ID]&_nonce=[NONCE]
Step 2: Inject XSS Payload (IDOR Update)
Modify the conditions of a template owned by the Administrator.
- Request:
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=ha_condition_update&template_id=[TARGET_ID]&conditions[0][0]=include&conditions[0][1]=general&conditions[0][2]=site&conditions[0][3]=1" onmouseover="alert(document.domain)" style="display:block;width:1000px;height:1000px;position:fixed;top:0;left:0;z-index:9999;"&_nonce=[NONCE] - Payload Explanation: The
conditions[0][3]value attempts to break out of an attribute (likelyvalue="..."ordata-id="...") and inject anonmouseoverevent. Thestyleattribute ensures the element covers the screen to trigger the event immediately upon mouse movement.
6. Test Data Setup
- Admin User: Default admin (ID 1).
- Contributor User:
wp user create attacker attacker@example.com --role=contributor --user_pass=password - Target Template: Create an
ha_librarypost as Admin.
Record the returned ID (e.g.,wp post create --post_type=ha_library --post_title="Sensitive Header" --post_status=publish --post_author=1123).
7. Expected Results
- The
ha_condition_updaterequest should return a JSON success message:{"success":true,...}. - When an administrator navigates to Happy Addons > Theme Builder and clicks "Edit Conditions" for the modified template, the
onmouseoverevent will trigger, executing the JavaScript.
8. Verification Steps
- Check Database State:
Verify the meta value contains the stringwp post meta get [TARGET_ID] _ha_template_conditionsonmouseover="alert(document.domain)". - Verify IDOR: Confirm the attacker (Contributor) was the one who performed the update even though they are not the author of the template.
9. Alternative Approaches
- Payload Variance: If
onmouseoveris filtered, tryontoggle,onfocus, oronerrorinside an<img>tag if the sink allows tag injection rather than just attribute injection. - Alternative Sink: Check if the conditions are rendered on the frontend. If the plugin uses these conditions to determine where to output a template, the
archiveorsingularcondition values might be rendered in the page's class list or data attributes on the frontend.
Summary
The Happy Addons for Elementor plugin (<= 3.21.0) is vulnerable to an Insecure Direct Object Reference (IDOR) that leads to Stored Cross-Site Scripting (XSS) due to an incorrect capability check (using the general 'edit_posts' instead of the object-specific 'edit_post') in the ha_condition_update AJAX action. This allows authenticated attackers with Contributor-level permissions to modify the display conditions of any template and inject malicious JavaScript via unescaped HTML attributes. The injected script executes when an administrator views the Template Conditions panel in the WordPress dashboard.
Vulnerable Code
// classes/condition-manager.php public function __construct() { // ... add_action('wp_ajax_ha_condition_autocomplete', [$this, 'process_autocomplete']); add_action('wp_ajax_ha_condition_update', [$this, 'process_condition_update']); add_action('wp_ajax_ha_cond_template_type', [$this, 'ha_get_template_type']); add_action('wp_ajax_ha_cond_get_current', [$this, 'ha_get_current_condition']); $this->process_condition(); } --- // Specific logic inferred from analysis (file truncated in source): // Location: classes/condition-manager.php public function validate_reqeust($template_id) { // Vulnerable check: 'edit_posts' is a general capability held by Contributors. // Passing $template_id is ignored by WordPress for this general capability string. return current_user_can('edit_posts', $template_id); }
Security Fix
@@ -218,7 +218,7 @@ public function validate_reqeust($template_id) { - return current_user_can('edit_posts', $template_id); + return current_user_can('edit_post', $template_id); } public function ha_get_current_condition() { $template_id = isset($_POST['template_id']) ? absint($_POST['template_id']) : 0; + if (!current_user_can('edit_post', $template_id)) { + wp_send_json_error(); + } // ... logic to return conditions }
Exploit Outline
The exploit targets the ha_condition_update AJAX endpoint using an authenticated session with Contributor-level privileges. 1. The attacker retrieves a valid security nonce from the HappyAddonsEditor JS object localized in the WordPress admin. 2. The attacker identifies the template_id of a template they do not own (e.g., a site-wide Header). 3. A POST request is sent to wp-admin/admin-ajax.php with the action 'ha_condition_update', specifying the target template_id and a 'conditions' array. 4. Within the conditions array, the attacker injects an XSS payload (e.g., 1" onmouseover="alert(1)") into a condition value. 5. Due to the IDOR in validate_reqeust(), the plugin allows the modification despite the attacker not being the template author. 6. When an administrator later opens the Theme Builder and clicks 'Edit Conditions' for that template, the unescaped payload is rendered into the DOM, executing the JavaScript in the administrator's context.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.