Hi! I recently get back to bug hunting, for fun rather than for profit, and I spent some time looking for an application that would match my tastes, which are an english website, no paid features, and some other things.
What I wanted the most was spending a lot of time on a small scope. Why? Because an almost fully automated process with subdomain enumeration and so on is not exciting at all for me. So I picked a program with small scope, and I started searching.
The application has an unusual registration process : pre-registration, then registration.
PRE-REGISTRATION
The preregistration feature is a single request sent with personal information I won’t describe, and especially an email. The server response contains a unique user_id for the newly created user. Note that the user_id cannot be predicted nor bruteforced.
REGISTRATION
In order to actually register, three requests must be sent :
- REQ1 — A first request containing user personal information including email, and their user_id obtained during pre-registration. The servers returns a user_token, which is used for the second request REQ2.
- REQ2— This second request is used to ask for a validation OTP and uses the user_token as an authorization bearer. An important detail : a different email can be specified to receive the OTP.
- REQ3 — Validate the OTP
- REQ4 — Set a password
What I just described is the normal way to proceed. The following image represents this process. Note that the scheme does not show the OTP procedure, because we don’t care.
So let’s ask a question : what happens if I try to register with an already registered user?
✅Step 1 — I create a user with the process described before.
❌Step 2 — Now, I do not pre-register again, but I try to send REQ1 to obtain a user_token because it is required for REQ2.
What happens is : nothing! REQ1 no longer returns the user_token we need for REQ2. This gives the following scheme.
At this points I tried to look for user_token string in Burp to see if I could retrieve it elsewhere, and the answer is yes! There was another request, let say REQX, that allowed me to get the user_token. So let’s start again!
✅Step 1 — I create a user with the process described before.
✅Step 2 — I use the new request REQX I found to obtain a user_token.
✅Step 3 — I send REQ2 with the user_token and it works! I can complete the OTP… This is useless.
Let’s start over with a slight difference : remember REQ2 normal behavior? We can change the email address for OTP. Let’s try it!
✅Step 1 — I create a user with the process described before.
✅Step 2 — I use the new request REQX I found to obtain a user_token.
✅Step 3 — I send REQ2 with the user_token and I provide another email address than mine!
⚠️Step 4 — Unexpected consequence : it updated my contact email address in the application! I can still log in with my original email address, but all communication is done with the new email I specified.
Answer to our question : what happens if I try to register with an already registered user? Well, the email address gets updated, even if I didn’t login!
So this is our updated scheme.
Ok, we didn’t hack anything yet since all testing was realized with my own account! How an attacker could exploit this? Let’s make some assumptions :
🥷Assumption 1 — The attacker knows its victim personal information. That actually very likely to happen, because of data leaks that sometimes contains millions of records.
🥷Assumption 2 — The attacker needs a user_token.
🥷Assumption 3 — The user_token could be obtained with REQ1 thanks to the user_id. But the request doesn’t work for an already registered user, and the attacker has no way to retrieve the user_id.
Well, I hid something from you… This is all about REQX : the other request I found the retrieve the user_token. The input needed by this request is only personal information… Here are the scheme and the steps.
✅Step 1 — The attacker gets its victim personal information.
✅Step 2 — The attacker sends REQX to get a user_token.
✅Step 3 — The attacker sends REQ2 with the user_token and provides their own email address!
✅Step 4 — The attacker triggers a password reset! (not shown on the scheme)
Before ending this post, you may wonder why I didn’t set the password after the OTP procedure, as intented by the application. The answer is that it simply didn’t work, so I went for a password reset instead.
Thanks for reading!