Unlimited Elements For Elementor <= 2.0.6 - Authenticated (Contributor+) Arbitrary File Read via Path Traversal in Repeater JSON/CSV URL with Path Traversal
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:NTechnical Details
<=2.0.6Source Code
WordPress.org SVNPatched version not available.
# 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_datapost 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:
- The plugin "Unlimited Elements" must be active.
- The attacker must use or create a widget that utilizes a "Repeater" field with "Remote Data" enabled.
- 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
- Entry Point: A widget is rendered on the frontend or inside the Elementor editor.
- Data Fetching: The widget checks if "Remote JSON/CSV" is enabled.
- Path Resolution:
- The user-provided URL (e.g.,
get_option('siteurl') . '/../../../../etc/passwd') is passed toURLtoRelative(). 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 prependsABSPATH.cleanPath()normalizes slashes but does not remove../.
- The user-provided URL (e.g.,
- File Sink: The resolved path is passed to a file-reading function like
file_get_contents()to retrieve the "JSON/CSV" data. - 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:
- Log in as a Contributor.
- Create a temporary post:
wp post create --post_type=post --post_status=publish --post_title="Exploit Page". - Navigate to the Elementor editor for that post:
browser_navigate("/wp-admin/post.php?post=ID&action=elementor"). - Extract the Elementor configuration nonces using
browser_eval:browser_eval("window.elementorConfig.nonces.save_builder")
- 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.
- Preparation: Identify a widget slug from Unlimited Elements that supports repeaters (e.g.,
uc_simple_listoruc_remote_json_tester). - Identify Meta Structure: Elementor stores data in the
_elementor_datapost meta as a JSON string. - 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).
- Update Post Meta: Use the
http_requesttool to send a POST request toadmin-ajax.phpwith theelementor_ajaxaction to save the builder data.- Action:
elementor_ajax - Actions Payload:
{"save_builder":{"action":"save_builder","data":{"status":"publish","elements":[...MALICIOUS_JSON...]}}}
- Action:
- Trigger Read: Navigate to the published post's URL. The rendering engine will execute the file read.
- Extract Data: Scrape the response body for the contents of
/etc/passwd.
6. Test Data Setup
- User: Create a Contributor user.
wp user create attacker attacker@example.com --role=contributor --user_pass=password
- Plugin Config: Ensure "Unlimited Elements" is active.
- Post: Create a post to be edited.
wp post create --post_type=post --post_status=publish --post_author=[USER_ID] --post_title="Traversal Test"
- Site URL: Determine the site URL for the payload prefix.
wp option get siteurl
7. Expected Results
- The
http_requesttoadmin-ajax.phpreturns{"success":true}. - When navigating to the post frontend, the HTML contains the string
root:x:0:0:root:/root:/bin/bashor the contents ofwp-config.php.
8. Verification Steps
- Meta Verification: Check if the malicious URL was successfully saved.
wp post meta get [POST_ID] _elementor_data
- Log Check: Check the WordPress debug log if the output isn't visible on the frontend.
tail -f wp-content/debug.log
- 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_ajaxrequest is too complex, usewp 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/passwdis restricted, attempt to readwp-config.phpby calculating the depth from thewp-content/plugins/unlimited-elements-for-elementor/directory.- Payload:
[SITE_URL]/../../wp-config.php
- Payload:
- 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="..."].
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
@@ -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.