CVE-2025-12845

Tablesome Table – Contact Form DB – WPForms, CF7, Gravity, Forminator, Fluent 0.5.4 - 1.2.1 - Missing Authorization to Authenticated (Subscriber+) Information Exposure and Privilege Escalation

highMissing Authorization
8.8
CVSS Score
8.8
CVSS Score
high
Severity
1.2.2
Patched in
1d
Time to patch

Description

The Tablesome Table – Contact Form DB – WPForms, CF7, Gravity, Forminator, Fluent plugin for WordPress is vulnerable to unauthorized access of data that leads to privilege escalation due to a missing capability check on the get_table_data() function in versions 0.5.4 to 1.2.1. This makes it possible for authenticated attackers, with Subscriber-level access and above, to retrieve plugin table data that can expose email log information. Attackers can leverage this on sites where the table log is enabled in order to trigger a password reset and obtain the reset key.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions>=0.5.4 <=1.2.1
PublishedFebruary 18, 2026
Last updatedFebruary 19, 2026
Affected plugintablesome

Source Code

WordPress.org SVN
Research Plan
Unverified

# Research Plan: CVE-2025-12845 Tablesome Privilege Escalation ## 1. Vulnerability Summary The **Tablesome Table** plugin (versions 0.5.4 - 1.2.1) contains a missing authorization vulnerability in the `get_table_data()` function. While the function is intended for administrative use to retrieve and…

Show full research plan

Research Plan: CVE-2025-12845 Tablesome Privilege Escalation

1. Vulnerability Summary

The Tablesome Table plugin (versions 0.5.4 - 1.2.1) contains a missing authorization vulnerability in the get_table_data() function. While the function is intended for administrative use to retrieve and display table records, it lacks a current_user_can() check. This allows any authenticated user (including Subscriber-level accounts) to retrieve the contents of any "Tablesome Table."

Because this plugin is often used to log contact form submissions and, in some configurations, system emails, an attacker can access sensitive information. Specifically, if the plugin is configured to log outgoing WordPress emails, an attacker can trigger a password reset for an administrator and then read the reset key/link directly from the logged table data, leading to full site takeover.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • AJAX Action: tablesome_get_table_data (inferred from function name) or tablesome_table_load_data.
  • Method: POST
  • Parameters:
    • action: tablesome_get_table_data (inferred)
    • table_id: The ID of the table to leak.
    • nonce: A valid security nonce (usually required for AJAX even if authorization is missing).
  • Authentication: Subscriber-level account required.
  • Precondition: A Tablesome table must exist that stores sensitive data (e.g., a "Form Submission" log or "Email Log").

3. Code Flow (Inferred)

  1. Entry Point: The plugin registers an AJAX action for logged-in users:
    add_action( 'wp_ajax_tablesome_get_table_data', 'get_table_data' );
  2. Vulnerable Function: Inside includes/tablesome-functions.php (or similar), the get_table_data() function is defined.
  3. Missing Check: The function likely verifies a nonce using check_ajax_referer() but fails to verify if the user has manage_options or similar administrative capabilities.
  4. Data Sink: The function proceeds to query the database for records associated with the provided table_id and returns them as a JSON response to the subscriber.

4. Nonce Acquisition Strategy

The Tablesome plugin typically enqueues its assets and nonces for logged-in users who can access the table editor or dashboard. Since the vulnerability is "Subscriber+", we can assume the nonce is available in the admin dashboard for these users.

  1. Create/Identify Page: Tablesome usually localizes a nonce in the WordPress admin area.
  2. Access Admin: Login as a Subscriber and navigate to /wp-admin/.
  3. Extract Nonce:
    • The plugin likely uses wp_localize_script.
    • The global JS object is often tablesome_obj or tablesome_vars.
    • Action: Use browser_eval to extract the nonce.
    • Target: window.tablesome_obj?.nonce or window.tablesome_vars?.ajax_nonce.

5. Exploitation Strategy

Step 1: Data Discovery (Table ID)

First, the attacker needs to find the ID of the table containing logs.

  1. Authenticate as a Subscriber.
  2. Use the http_request tool to check for existing tables if an endpoint like tablesome_get_tables is also exposed, or simply brute-force small integer IDs (1, 2, 3...) for table_id.

Step 2: Trigger Password Reset

  1. Request a password reset for the admin user via /wp-login.php?action=lostpassword.
  2. This sends an email containing the reset link.

Step 3: Leak the Reset Key

  1. Perform the AJAX request to leak table data.
  2. Request:
    POST /wp-admin/admin-ajax.php HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    
    action=tablesome_get_table_data&table_id=1&nonce=[EXTRACTED_NONCE]
    
  3. Response: A JSON object containing rows of table data.
  4. Search the response for the text "password reset" or "wp-login.php?action=rp&key=".

Step 4: Privilege Escalation

  1. Use the leaked key to set a new password for the admin.
  2. Log in as the administrator.

6. Test Data Setup

To simulate the vulnerable environment:

  1. Install Plugin: Tablesome version 1.2.1.
  2. Create Table:
    • wp eval "/* Code to create a Tablesome table with ID 1 */"
    • Alternatively, use the UI to create a table named "Email Logs".
  3. Enable Logging: In Tablesome settings, ensure that contact form submissions or WordPress emails are directed to "Table 1".
  4. Create Users:
    • Administrator: admin
    • Subscriber: attacker
  5. Shortcode Page: (If needed for nonce)
    wp post create --post_type=page --post_status=publish --post_title="Tablesome" --post_content='[tablesome table_id="1"]'

7. Expected Results

  • The get_table_data request returns a 200 OK status.
  • The response body contains JSON data representing the rows of the table.
  • The table data includes entries for the password reset email sent to the administrator, including the key and login parameters.

8. Verification Steps

  1. Verify Exposure: Check if the JSON response contains the admin's email content.
  2. Verify Takeover: After using the leaked key to reset the password, attempt to login as admin with the new password.
  3. Database Check: Use wp user get admin to confirm the account still exists and has the administrator role.

9. Alternative Approaches

  • REST API: Check if the plugin registers REST routes under wp-json/tablesome/v1/. If so, check for missing permission_callback on table data endpoints.
  • Table Brute Force: If table_id=1 is empty, iterate through table_id=1 to table_id=20 to find where the logs are stored.
  • Direct Database ID: If the plugin uses UUIDs instead of integer IDs, look for an endpoint that lists available table headers/metadata accessible to Subscribers.
Research Findings
Static analysis — not yet PoC-verified

Summary

The Tablesome plugin fails to perform authorization checks in its AJAX data retrieval function, allowing any authenticated user with Subscriber-level access or higher to access the contents of any table managed by the plugin. If the site uses Tablesome to log emails or contact form submissions, an attacker can leak sensitive information such as password reset keys to escalate privileges to Administrator.

Vulnerable Code

// From includes/tablesome-functions.php (inferred from research plan and plugin structure)

add_action( 'wp_ajax_tablesome_get_table_data', 'get_table_data' );

function get_table_data() {
    // Verifies nonce but fails to verify user capabilities
    check_ajax_referer( 'tablesome_nonce', 'nonce' );

    $table_id = isset( $_POST['table_id'] ) ? sanitize_text_field( $_POST['table_id'] ) : '';
    
    // Proceeding to fetch and return table data without checking if current_user_can('manage_options')
    $records = Tablesome_DB::get_table_records( $table_id );
    
    wp_send_json_success( $records );
}

Security Fix

--- includes/tablesome-functions.php
+++ includes/tablesome-functions.php
@@ -2,6 +2,10 @@
 function get_table_data() {
     check_ajax_referer( 'tablesome_nonce', 'nonce' );
 
+    if ( ! current_user_can( 'manage_options' ) ) {
+        wp_send_json_error( array( 'message' => 'Unauthorized' ), 403 );
+    }
+
     $table_id = isset( $_POST['table_id'] ) ? sanitize_text_field( $_POST['table_id'] ) : '';
     
     $records = Tablesome_DB::get_table_records( $table_id );

Exploit Outline

1. Login to the target site with Subscriber-level credentials. 2. Locate the AJAX security nonce (typically found in the 'tablesome_vars' or 'tablesome_obj' JavaScript objects localized in the WordPress admin dashboard). 3. Trigger a password reset for the target Administrator account via '/wp-login.php?action=lostpassword' to generate a reset key likely to be logged in a Tablesome table. 4. Craft a POST request to '/wp-admin/admin-ajax.php' using the action 'tablesome_get_table_data', the extracted nonce, and a 'table_id' (iterating through small integers if the ID is unknown). 5. Parse the JSON response for table rows containing outgoing email content, specifically looking for the password reset link (containing 'action=rp&key='). 6. Navigate to the leaked reset link to set a new password for the Administrator account and take over the site.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.