The Curiosity Behind Authentication Vulnerabilities
HackenProof triage team note:
Gowtham is always curious to dig into these authentication vulnerabilities. This unique finding leads him to take over any user-account without any user-interaction and can lead to huge loss of user funds and companies’ reputation. We were really impressed with his work when we received that report with CVSS score:10.
Standard Authentication Flow
Let’s start with the general authentication flow:

- In Step 1: User sends his credentials (Username + Password) to the Server.
- In Step 2: Server requests the database for password for the given username.
- In Step 3: Database sends back respective password of that email/username.
- In step 4: The Server makes a validation. If the given password matches with the (3), it sends back AUTH token or cookie. If not, the server throws an error stating “Username/Password are mismatched”.
At this stage, you understood how generally an authentication takes place.
Crypto-exchange Authentication
Now, let’s understand how this crypto-exchange authentication works.

- Step 1: User enters his Username and Password and clicks on “login.”
- Step 2: Once the credentials are verified, the server responds back with “Temp_Token(Temporary Token)”. At the stage, the server sends an OTP to the connected email.
[Acts as 2FA]
- Step 3: Now user enters the OTP that he received on his email and clicks on Submit.
- Step 4: Server validates the OTP. If the OTP matches with the generated OTP (in the backend) the Server responds back with Auth_Token(Authentication Token) along with session cookie.
The Forgot Password Mechanism
The question arises, what if user forgets his password? Simple, he requests for “Forgot-Password”. So, let’s also understand how it works.

- Step 1: User enters his “email” and clicks on Reset-Password.
- Step 2: The Server responds back with a “Temp_Token”. At this stage, the server also generates an OTP and sends to the email.
- Step 3: Now user enters the new password along with the OTP he received on his email.
- Step 4: If the OTP matches with the “Temp_Token”, the Server returns success.
Decoding the Vulnerability: A Practical Scenario
This vulnerability is existed due to a miss in logical flow of the application. Let’s see what I practically did in my scenario.
While doing any web-application pentesting [OR] Bug-Bounty in general, I create Two accounts. So even in this scenario, I created two accounts and for simplicity’s sake, let’s call them as
Step 1: Login to my [email protected] with the password.
[Received a Temp_user_id]
Step 2: Enter the OTP that I received on my mail address [email protected]

[Received web_socket token along with Session Cookie] – Validated.
Step 3: Open a private tab and click on “Forgot-Password” and enter your victim email address along with the new password.

[Received a temp_user_id]
[ For POC Sake, We used “support@company,com” email on the forgot-password end-point ]
- At this stage, if you clearly look into the requests and response, the common temp_user_id is being generated on endpoints (/login and /forgot-password). So I ended up replacing the temp_user_ids in the Step 2.
Step 4: Send the Step 2 Request to repeater and modify the temp_user_id with the one you got in Step 3]

[ The Server responds with web_socket_token along with the Session Cookie] – Attack Successfully exploited.
Understanding the Limitations
[Note] – To Notify you, this attack doesn’t work if you follow exactly what I said. There are few limitations of this attack.
- You must validate the 2FA step with the correct OTP in the Step 2 with your temp_user_id.
Behind the Scenes: Assumed Backend Logic
“As I’m not aware of the backend code, this is all my assumptions on the functionality is coded. I might be correct and wrong. “
So for the first time in the Step 2, the server is checking whether the code generated for the temp_user_id is matching with the OTP entered, If both are same, it returns true. And from the next time, It doesn’t matter which temp_user_id you provide, it returns true unless the OTP that is entered is not modified.
For simplicity, this is how code might look in the backend:
[coded using ChatGPT]
function validateOTP($tempUserID, $otp, $firstTimeVerification)
{
global $conn;
$sql = "SELECT * FROM users WHERE temp_user_id = '$tempUserID'";
$result = $conn->query($sql);
if ($result->num_rows === 1) {
$row = $result->fetch_assoc();
$storedOTP = $row["otp"];
if ($firstTimeVerification) {
// For the first time verification, compare the
// OTP with the stored OTP for the given tempUserID
if ($otp === $storedOTP) {
return true;
} else {
return false;
}
} else {
// For subsequent verifications, compare the
// OTP with the stored OTP without considering the tempUserID
if ($otp === $storedOTP) {
return true;
} else {
return false;
}
}
}
return false; // Invalid temporary user ID
}
EndNote: The Responsibility of a Security Researcher
Even though the authentication mechanism is followed correctly, the developers tend to make out mistakes. As a Security Researcher, it is our responsibility to report them and make our internet more secure.