Mass Account Takeover via simple IDOR and funny OTP Bypass

1 month ago 25
BOOK THIS SPACE FOR AD
ARTICLE AD

Mani Bharathi B

Hi guys, I’m Mani Bharathi, a web app pentester who does a little bit of bug hunting in my free time. I’ve always wanted to write about my findings, and I’m excited to share my first public write-up.

By DALL E for thumbnail

I reported a critical vulnerability few months ago to a company ,where I was able to take over any account via an IDOR and OTP bypass. Lets get into it.

My simple methodology whenever testing web apps is using all the functionalities and looking at the HTTP history in burp for interesting requests(I don’t know how to recon). I created an account (Vick Team) and found an unauthenticated API in the Backend — target.execute-api.us-west-2.amazonaws.com

And found an interesting request in the history from the API endpoint /dev/auth/get-user-by-id with an IDOR vulnerability leaking user’s PII, auth_token, password hashes and much more.

We could also change the username and auth_token using IDOR:

I was too lazy to try brute-forcing the bcrypt hash. So I looked for something else and saw the password reset token with empty value in IDOR #1.

Now we can get any user’s Email ID with IDOR #1 and use it to generate password reset token via forgot password functionality (shown below). And using the same IDOR #1 to get the password reset token and change the password with URL: https://target.com/changepassword?token=0b74bef3-1e7b-43b3-8b20-d04b7b8798ae

I changed the password of my Vick Team’s account and tried to login but the app asked for OTP. I was confused as when I logged in before there was no OTP required , This is where the auth_token comes in. The auth_token contains the user’s IP address and when it doesn’t match with the current auth_token which contains the current IP address when logging in, there’s 2FA.

Data inside auth_token

But surely using MFA makes everything safe right?

made with pinatafarm.com

I went to see the HTTP history in Burp and the OTP was inside a request. The OTP was automatically generated and validated client-side and then sent over in the request to the server for sending OTP email. I could use this OTP to login.

And just like that I was able to take over my own account.

I found the vulnerable Javascript code in the browser and let ChatGPT explain it

let a = Math.floor(1e6 * Math.random()); //This line generates a random 6-digit number (1e6 * Math.random() returns a number between 0 and 999999, and Math.floor() rounds it down to the nearest integer). This is a simple way of generating a 6-digit OTP.R(a.toString()); //The generated OTP is being converted to a string and possibly stored or used elsewhere. The R() function is likely responsible for saving or handling the OTP locally.await (0, m.OA)(a, e.email); //This line sends the generated OTP (a) to the user's email (e.email) using the function m.OA(). This function is presumably responsible for interacting with the backend or email service to send the OTP.

And It also confirmed my theory, why the app required OTP:


a({
type: "LOGIN_SUCCESS",
payload: {
isLogin: !0,
userInfo: s.data,
userTocken: s.data.auth_token
}
});
let l = await (0,
m.C8)()
, n = await (0,
m.F8)(i.auth_token);
if (l.ip !== n.ipAddress) {
P(!0),
await (0,
m.TP)(i.id, {
auth_token: i.auth_token
});

Before the OTP part, the system checks the user’s IP address (l.ip and n.ipAddress) to detect potential IP mismatches, likely to enforce additional security. If the IP addresses are different, it triggers the OTP process for additional verification. - ChatGPT

I wondered what would happen if I sent a large OTP to the server.

And It came back as infinity. 😅

From: info@target.com
Reply-To: info@target.com
To: myemail@domain.com
Subject: OTP for Login
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 7bit
Message-ID: <12345@us-west-2.amazonses.com>

<p>Your OTP for Logging in is Infinity</p>

EmailAlways enforce server-side authorization checks to ensure that a user can access or modify only resources they are authorized to.Avoid using sequential or predictable user identifiers (like 8000, 8001, etc.) in API endpoints. Use UUIDs or random, non-sequential identifiers to make it much harder for attackers to guess or enumerate valid IDs.OTP generation and validation should only happen on the server. If the OTP is generated client-side , attackers can intercept or modify it before it’s sent to the server.
Read Entire Article