CVE-2026-4659

Unlimited Elements For Elementor <= 2.0.6 - Authenticated (Contributor+) Arbitrary File Read via Path Traversal in Repeater JSON/CSV URL with Path Traversal

highImproper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
7.5
CVSS Score
7.5
CVSS Score
high
Severity
2.0.7
Patched in
8d
Time to patch

Description

The Unlimited Elements for Elementor plugin for WordPress is vulnerable to Arbitrary File Read via the Repeater JSON/CSV URL parameter in versions up to, and including, 2.0.6. This is due to insufficient path traversal sanitization in the URLtoRelative() and urlToPath() functions, combined with the ability to enable debug output in widget settings. The URLtoRelative() function only performs a simple string replacement to remove the site's base URL without sanitizing path traversal sequences (../), and the cleanPath() function only normalizes directory separators without removing traversal components. This allows an attacker to provide a URL like http://site.com/../../../../etc/passwd which, after URLtoRelative() strips the domain, results in /../../../../etc/passwd being concatenated with the base path and ultimately resolved to /etc/passwd. This makes it possible for authenticated attackers with Author-level access and above to read arbitrary local files from the WordPress host, including sensitive files such as wp-config.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=2.0.6
PublishedApril 16, 2026
Last updatedApril 24, 2026

Source Code

WordPress.org SVN
Patched

Patched version not available.

Research Plan
Unverified

# Exploitation Research Plan: CVE-2026-4659 (Unlimited Elements For Elementor) ## 1. Vulnerability Summary The **Unlimited Elements For Elementor** plugin (<= 2.0.6) contains an authenticated arbitrary file read vulnerability. The flaw exists in how the plugin converts a provided "Repeater JSON/CSV…

Show full research plan

Exploitation Research Plan: CVE-2026-4659 (Unlimited Elements For Elementor)

1. Vulnerability Summary

The Unlimited Elements For Elementor plugin (<= 2.0.6) contains an authenticated arbitrary file read vulnerability. The flaw exists in how the plugin converts a provided "Repeater JSON/CSV URL" into a local file path. Specifically, the URLtoRelative() function removes the site's base URL via simple string replacement without sanitizing for path traversal (../) sequences. When this processed string is passed to urlToPath(), it is concatenated with the site's base path and resolved. An attacker can provide a URL pointing to the local site followed by traversal sequences (e.g., https://example.com/../../../../etc/passwd) to read any file on the server that the PHP process has permission to access.

2. Attack Vector Analysis

  • Endpoint: Elementor Editor / Post Save (via _elementor_data post meta) or the widget rendering engine.
  • Vulnerable Parameters: Any widget field that utilizes the "Repeater" with "Remote JSON/CSV" functionality. Specifically, the parameter used to store the remote URL.
  • Authentication: Contributor-level access or higher is required to edit posts/pages and use Elementor widgets.
  • Preconditions:
    1. The plugin "Unlimited Elements" must be active.
    2. The attacker must use or create a widget that utilizes a "Repeater" field with "Remote Data" enabled.
    3. The widget's debug mode or data display must be configured to output the fetched content (or the content must be rendered in the widget).

3. Code Flow

  1. Entry Point: A widget is rendered on the frontend or inside the Elementor editor.
  2. Data Fetching: The widget checks if "Remote JSON/CSV" is enabled.
  3. Path Resolution:
    • The user-provided URL (e.g., get_option('siteurl') . '/../../../../etc/passwd') is passed to URLtoRelative().
    • URLtoRelative() (inferred file: provider/core/framework/helpers/unite_functions.class.php) performs: str_replace(get_option('siteurl'), '', $url).
    • Result: /../../../../etc/passwd.
    • urlToPath() takes this result and prepends ABSPATH.
    • cleanPath() normalizes slashes but does not remove ../.
  4. File Sink: The resolved path is passed to a file-reading function like file_get_contents() to retrieve the "JSON/CSV" data.
  5. Output: If the "Debug" option is enabled in the widget settings, or if the file content is mapped to a visible field in the repeater, the contents of the file are echoed to the page.

4. Nonce Acquisition Strategy

Accessing the Elementor editor as a Contributor requires standard WordPress authentication. Saving and rendering Elementor widgets requires the elementor_ajax nonce or the post-specific nonce.

Strategy:

  1. Log in as a Contributor.
  2. Create a temporary post: wp post create --post_type=post --post_status=publish --post_title="Exploit Page".
  3. Navigate to the Elementor editor for that post: browser_navigate("/wp-admin/post.php?post=ID&action=elementor").
  4. Extract the Elementor configuration nonces using browser_eval:
    • browser_eval("window.elementorConfig.nonces.save_builder")
  5. Unlimited Elements also localizes data. Check for:
    • browser_eval("window.unite_admin_options?.nonce") (inferred localization key).

5. Exploitation Strategy

The goal is to update a post's Elementor data to include a widget that attempts to read /etc/passwd.

  1. Preparation: Identify a widget slug from Unlimited Elements that supports repeaters (e.g., uc_simple_list or uc_remote_json_tester).
  2. Identify Meta Structure: Elementor stores data in the _elementor_data post meta as a JSON string.
  3. Construct Payload:
    • Create a JSON structure for an Elementor page containing one Unlimited Elements widget.
    • In the widget settings, set the remote URL parameter to: [SITE_URL]/../../../../../../../../etc/passwd.
    • Set the "use_remote_data" flag to true.
    • Set the "debug_mode" or "show_errors" flag to true (if available).
  4. Update Post Meta: Use the http_request tool to send a POST request to admin-ajax.php with the elementor_ajax action to save the builder data.
    • Action: elementor_ajax
    • Actions Payload: {"save_builder":{"action":"save_builder","data":{"status":"publish","elements":[...MALICIOUS_JSON...]}}}
  5. Trigger Read: Navigate to the published post's URL. The rendering engine will execute the file read.
  6. Extract Data: Scrape the response body for the contents of /etc/passwd.

6. Test Data Setup

  1. User: Create a Contributor user.
    • wp user create attacker attacker@example.com --role=contributor --user_pass=password
  2. Plugin Config: Ensure "Unlimited Elements" is active.
  3. Post: Create a post to be edited.
    • wp post create --post_type=post --post_status=publish --post_author=[USER_ID] --post_title="Traversal Test"
  4. Site URL: Determine the site URL for the payload prefix.
    • wp option get siteurl

7. Expected Results

  • The http_request to admin-ajax.php returns {"success":true}.
  • When navigating to the post frontend, the HTML contains the string root:x:0:0:root:/root:/bin/bash or the contents of wp-config.php.

8. Verification Steps

  1. Meta Verification: Check if the malicious URL was successfully saved.
    • wp post meta get [POST_ID] _elementor_data
  2. Log Check: Check the WordPress debug log if the output isn't visible on the frontend.
    • tail -f wp-content/debug.log
  3. File Content Verification: Compare the exposed content with the actual file on the system (if in a controlled environment).

9. Alternative Approaches

  • Direct Meta Update: If the elementor_ajax request is too complex, use wp post meta update [ID] _elementor_data '[JSON]' via the shell to set up the state, then use the browser to simply view the result.
  • Different File: If /etc/passwd is restricted, attempt to read wp-config.php by calculating the depth from the wp-content/plugins/unlimited-elements-for-elementor/ directory.
    • Payload: [SITE_URL]/../../wp-config.php
  • Shortcode Vector: Some Unlimited Elements widgets can be triggered via shortcode. Attempt to inject the traversal via a shortcode attribute if the widget supports it: [uc_widget_name remote_url="..."].
Research Findings
Static analysis — not yet PoC-verified

Summary

The Unlimited Elements for Elementor plugin is vulnerable to authenticated arbitrary file read due to insufficient sanitization of the 'Repeater JSON/CSV URL' parameter. An attacker with Contributor-level access or higher can use path traversal sequences (../) in a URL pointing to the local site, causing the plugin to resolve and read sensitive local files like wp-config.php and display their contents.

Vulnerable Code

// provider/core/framework/helpers/unite_functions.class.php

public static function URLtoRelative($url) {
    $siteUrl = get_option('siteurl');
    // Vulnerable: performs simple string replacement without checking for traversal sequences like '../'
    $relative = str_replace($siteUrl, '', $url);
    return $relative;
}

---

public static function urlToPath($url) {
    $relative = self::URLtoRelative($url);
    $path = ABSPATH . $relative;
    return self::cleanPath($path);
}

---

public static function cleanPath($path) {
    // Vulnerable: only normalizes directory separators
    $path = str_replace('\\', '/', $path);
    $path = str_replace('//', '/', $path);
    return $path;
}

Security Fix

--- provider/core/framework/helpers/unite_functions.class.php
+++ provider/core/framework/helpers/unite_functions.class.php
@@ -10,6 +10,7 @@
 	public static function URLtoRelative($url) {
 		$siteUrl = get_option('siteurl');
 		$relative = str_replace($siteUrl, '', $url);
+		$relative = str_replace('..', '', $relative);
 		return $relative;
 	}
 
@@ -20,6 +21,11 @@
 		$path = ABSPATH . $relative;
-		return self::cleanPath($path);
+		$path = self::cleanPath($path);
+		if (strpos($path, ABSPATH) !== 0) {
+			return null;
+		}
+		return $path;
 	}

Exploit Outline

1. Authenticate to the WordPress site as a Contributor or higher. 2. Create a new post and open it in the Elementor editor. 3. Add an Unlimited Elements widget that supports the 'Repeater' functionality with 'Remote Data' enabled (e.g., Simple List). 4. Locate the configuration field for 'Repeater JSON/CSV URL'. 5. Set the URL value to the site's base URL followed by path traversal sequences and the target file path (e.g., http://example.com/../../../../wp-config.php). 6. Enable 'Debug Mode' or 'Show Errors' within the widget settings to ensure the fetched content is output to the browser. 7. Save the post or trigger a preview; the plugin will strip the base URL, concatenate the traversal string with the absolute path, and read/display the contents of the target file via the widget's render output.

Check if your site is affected.

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