CVE-2026-3655

OTP Login With Phone Number, OTP Verification <= 1.8.60 - Unauthenticated Authentication Bypass via Firebase OTP Verification

criticalImproper Authentication
9.8
CVSS Score
9.8
CVSS Score
critical
Severity
1.8.61
Patched in
1d
Time to patch

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:H
Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
High
Confidentiality
High
Integrity
High
Availability

Technical Details

Affected versions>=1.8.50 <=1.8.60
PublishedMay 28, 2026
Last updatedMay 29, 2026

What Changed in the Fix

Changes introduced in v1.8.61

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

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 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 the lwp_ajax_register handler and the idehweb_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 the username (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 both wp_ajax_ and wp_ajax_nopriv_ in login-with-phonenumber.php).
  • Authentication: Unauthenticated.
  • Payload Parameters:
    • action: lwp_ajax_register
    • username: The victim's phone number (stored in phone_number user meta).
    • method: firebase
    • nonce: A valid WordPress nonce for the action lwp_login (extracted from the frontend).
    • idToken: A valid Firebase Identity Token obtained by the attacker for their own phone number.

3. Code Flow

  1. Entry Point: An unauthenticated user sends a request to admin-ajax.php with action=lwp_ajax_register.
  2. Nonce Verification: The handler verifies the nonce provided in the request (likely using the lwp_login action string, as seen in lwp_ajax_login).
  3. Method Selection: The handler checks the method parameter. If set to firebase, it calls idehweb_lwp_activate_through_firebase($_POST['idToken']).
  4. Firebase Validation: idehweb_lwp_activate_through_firebase calls the Google Firebase API to verify the token. It receives a response containing the phoneNumber verified by Firebase.
  5. Logic Flaw: The handler receives confirmation that a phone number was verified but does not compare the verified phone number with the username parameter provided in the request.
  6. Authentication: The handler uses the provided username (the victim's phone number) to find the user ($this->phone_number_exist($phone_number)).
  7. 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.

  1. Identify Localization: In inc/frontend-functions.php, the function enqueue_scripts() registers the script idehweb-lwp and uses wp_localize_script() to pass data to JavaScript.
  2. JS Variable: The localized object is named userspn_ajax.
  3. Nonce Key: The nonce is stored in userspn_ajax.nonce.
  4. 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").

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_number meta.

Steps

  1. Obtain Victim Phone Number: Identify the phone number of the target administrator.
  2. 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.
  3. Construct Payload:
    • action: lwp_ajax_register
    • username: [VICTIM_PHONE_NUMBER]
    • method: firebase
    • nonce: [EXTRACTED_NONCE]
    • idToken: [ATTACKER_VALID_TOKEN]
  4. Send Request: Use http_request to send a POST request to admin-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

  1. Create Admin: wp user create victim_admin admin@example.com --role=administrator --user_pass=password
  2. Set Phone Number: wp user meta set victim_admin phone_number 1234567890
  3. Configure Plugin: Enable Firebase (requires setting options in idehweb_lwp_settings via wp option update).
    • idehweb_store_number_with_country_code = 1
    • idehweb_default_gateways = ['firebase']
    • idehweb_use_custom_gateway = 1
  4. 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-Cookie headers for the target administrator (wordpress_logged_in_...).
  • Subsequent requests to /wp-admin/ using the returned cookies should grant administrative access.

8. Verification Steps

  1. Check Cookies: Verify the http_request response contains authentication cookies.
  2. 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.
  3. Database State: Check wp_user_meta to 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 in inc/ajax-handlers.php). If it also handles Firebase verification via idehweb_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.
Research Findings
Static analysis — not yet PoC-verified

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

--- /home/deploy/wp-safety.org/data/plugin-versions/login-with-phone-number/1.8.60/inc/ajax-handlers.php	2026-02-07 06:44:48.000000000 +0000
+++ /home/deploy/wp-safety.org/data/plugin-versions/login-with-phone-number/1.8.61/inc/ajax-handlers.php	2026-03-10 17:29:54.000000000 +0000
@@ -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.