Permanent account takeover on Yahoo's Small Business platform
If you decided to go out and spontaneously develop a content management system one of the most crucial and necessary setups would be the authentication of user accounts. This portion of the website would be designed to enable the successful login and session establishment of users who are who they say they are, and disable access to user accounts from people who aren't who they say they are. This function is generally accomplished through designation of a username and password (normally created by the user), but can get messy when you have to deal with real world scenarios.
Forgot password
During my nightly bug bounty session I decided to take a look at how Luminate handles forgotten passwords. As expected, they had a method to reset user passwords.
The flow of events for the page is as follows:
- The user submits a request with their email notifying the server they forgot their password
POST /forgotpassword HTTP/1.1 Host: login.luminate.com ontent-Type: application/x-www-form-urlencoded Content-Length: 861 Connection: close Upgrade-Insecure-Requests: 1 email=example@example.com
- The server will create a one-time token to be sent to a contact address tied to the user
https://login.luminate.com/passwordreset?sign=TMaJJnAjigfnprxqbcfnuBK8eJmJL2PHFByAA8OblfyHdZvxhXkeTmo5G_V1TNabJHUmSR9OSeYAnzm-yAlKbUfCYLsCQtrZnZF2IxCotLh_VEn7Px6nVTA3Sm_fF9t490t_x9-t1xKcVqRPLOgQGSHb3wXYBevsypDblPoO1c4
- The user will use this one-time token to verify they are who they say they are and reset their password
POST /passwordreset HTTP/1.1
Host: login.luminate.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Content-Type: application/x-www-form-urlencoded
Content-Length: 463
onnection: close
Upgrade-Insecure-Requests: 1
password=password&cpassword=password&**uuid=6491c80b-2850-4d9c-9061-73a6122b3dca**&**sign=TMaJJnAjigfnprxqbcfnuBK8eJmJL2PHFByAA8OblfyHdZvxhXkeTmo5G_V1TNabJHUmSR9OSeYAnzm-yAlKbUfCYLsCQtrZnZF2IxCotLh_VEn7Px6nVTA3Sm_fF9t490t_x9-t1xKcVqRPLOgQGTiD-OCPPqBlpAWpi4yXgz0**&email=example@example.com
Experimentation
The password reset method in question was very interesting because it included additional parameters that weren't necessary. If you look at step two, the "sign" parameter is the secret key passed via email that protects the user against attackers using predictable data to reset their password. This is the only necessary parameter for a password reset since any additional data would obfuscate or botch the system.
Looking at step three we can see that the actual request to reset the password gives the user options to modify things like "email" and "uuid". When I saw this I was really interested, but assumed that the server would ask "does that signature match the user ID being reset?".
After a little bit of poking I realized that changing the email parameter by itself didn't do anything. It appeared that the "email" parameter was just a visual aid.
There was still something about the request that bothered me.. what is the "uuid" parameter?
Why would the client have control over a unique user ID when resetting a password if the "sign" parameter was all that was necessary? From a programming standpoint I felt the developer would have to go above and beyond to create a system that would identify and fetch data using the sign parameter, print the fetched data into a hidden field, then parse this data and re-examine it at a later point in time.
<input name="uuid" value="6491c80b-2850-4d9c-9061-73a6122b3dca" type="hidden">
Attacking the discovered issue
From assumption and past experience I made the conclusion the "uuid" parameter was actually a unique user ID tied to the users account (go figure). If this scenario was exploitable I predicted that I could supply other peoples user IDs and reset their passwords without a matching sign parameter.
In order to test this I created an attacking account that we'll call "attacker@attacker.com". This accounts goal was to create a reset password token that would belong to "_attacker@attacker.com (UUID 1231c32b-2850-4e9c-9061-42k3022b3dcd)" but then modify the "uuid" parameter in transit to match "_samwcurry@gmail.com (UUID 6491c80b-2850-4d9c-9061-73a6122b3dca)".
After following the standard order of events to reset a password (steps 1-2 above) I made it to the final screen where I would supply my new password and submit the changes. Using BURP suite I modified the "uuid" parameter to that of the victim account and submitted an almost identical POST request to step three... but on a different session with a different sign parameter.
The result of this request?
Success. It appeared that the "uuid" parameter was (1) a unique identifier belonging to each account, (2) able to be modified by the client when submitting a password reset, and (3) not tied to the "sign" parameter during password resets.
Exploitation
Taking us back to the top of this article, the way in which users say who they are is through the username and the way in which they prove who they are is through the password. If we can compromise the password, then we can compromise the account.
The unique user ID is tied to the victims account. The unique user ID can be paired with the "password reset" function to create new passwords.
If known -- the unique user ID serves as a permanent gateway into the account.
This vulnerability presents a major issue in Yahoo's business platform due to the foothold capability is has on someones account. There is no way to stop attackers from using this unique used ID over and over again to completely deny access to an account and eventually fully takeover an account.
Timeline
- June 14th, 2017 - Reported to vendor
- June 14th, 2017 - Triage/validated as a vulnerability
- June 15th, 2017 - Resolved
- June 25th, 2017 - $4,000 bounty