Breaking Down the Fortress: A Teen’s Unveiling of a Critical Bug in a High-Stakes Cryptoexchange

Gowtham Naidu Ponnana
5 Minutes Read

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.

Read more on HackenProof Blog