Recently, a friend of mine (from outside the United States) had a third party make them a website for eCommerce sales and blogging. Because of this, they wanted me to do a quick review of the security of the application. After reviewing the account handling on the application I determined that the Password Reset functionality was the worst implementation ever made. If you can think of an issue I bet this had it... Below, we will be going into what went wrong and how to find these types of issues yourself.
Back to the Basics
If accounts exists on a website there are some basic features of it:
- Create Account
- Forgot Password/ Password Reset
- Update Account
- Delete Account
- View Account
If any of these is done in insecure way, major havoc can occur. In this blog post, we will be paying special attention to the Password Reset
functionality of a website and how it can go wrong.
Basic Flow of Password Reset
What do you do if a user forgets their password? Well, you want them to be able to login, but you still want to make sure that it is the correct user. So, it is time to rest their password.
Generally, a users account is tied to an email. With this, the website can send this users email a magic link. This magic link deems that the user is trusted and will allow them to change the password.
Once the link is clicked on, a user can select their new password. When the user sends the new password to the backend, the magic value is sent back along with it. This is so that the magic value can be verified from the link to ensure this is the correct user. With the correct value, the password is reset and the user can continue! What could possibly go wrong?
What Went Wrong #1 - Email Enumeration (Low)
From the very beginning, the reset functionality had issues. The initial request gave two different messages, depending on if the email was valid or invalid. The initial request, with an improper email, gave back the following error message:
Incorrect Email Message
With a valid email, it just said that the email had been sent.
But, why is this bad? Because it is trivial to distinguish between a valid and invalid email, this can provide an easy way for email enumeration of users with an account. This by itself is not a huge deal, but can be used in further attacks and information leakage.
What Went Wrong #2 - Host Header Injection (High)
Now, this time, we are going to input a valid email address. Everything works are expected... So, let's tamper with the request itself!
HTTP sends a plethora of different headers on each request, such as the Origin, Cookies, Content-Type and more. The important one for this attack is the Host Header. The Host Header is defined as
Specifies the domain name of the server.
by Mozilla. In layman's terms, this simply means the host name (domain) without the protocol.
Even though the Host is set in the browser on a normal request (and cannot be altered), software like Burp and Curl can trivially spoof header/value in a request made to the backend. If the backend is trusting any of these values in an improper way, havoc can unfold.
The Host Header is sometimes used in order to create links from it. So, what if we alter the host header? It turns out that by altering the Host header, the domain of the link can be changed! For example, see the following screenshot:
Host Header Injection
What's the attack here though? Well, this means that when the link is clicked (in the users email), the redirect (along with the magic value) will be sent to an attacker controlled host! Once the link has been clicked, the attacker can simply use this reset token and reset the users password. Below is an example of the password reset link, after the host header modification, with the magic value:
Email Reset Link
What Went Wrong #3 - Short Reset Token (Medium/High)
If we ignore the first issue (that takes a single click from the user) what else is out there? Immediately, when I hovered over the reset link, I noticed that only 6 digits were being used for the token. SIX.
Mathematically, this is 10^6 or 1,000,000 chances. With no throttling and a fairly long expiration time on the tokens (which are both issues in their own right), this is possible to brute force from a single computer.
By requesting a reset email for a single user then attempting each of the tokens in the set, it would be possible to reset the users password! Although this would take some time to perform, it is absolutely a viable attack method for taking over an account.
What Went Wrong #4 - Lack of Server Side Validation (Critical)
You are probably thinking 'There are already two ways to take over the account, what else could go wrong?' You are in luck though, because there is one more major bug.
Upon clicking on the link to go back to the website, the magic token was validated. Now, all the user has to do is send the new password back to the backend. Do you see anything wrong with this?
Lack of Validation of Token
The first request validates the password reset token. But, the second request, where the password was actually being reset, did not include the magic value! Because of this, a single request could reset any users password; it was really as simple as that! A picture of the request in Burp is shown below:
Password Reset Request
Notice that the above request does not include the magic value
inside of it. Boom, a single request to compromise the account: no clicks or brute forcing needed.
The creators of the website were from a third world country where English is not the primary language and the people only speak a little English. So, when looking for resources in their language, we could not find very much about the vulnerabilities discussed above. In particular, there was nothing online referring to Host Header Injection. To solve this problem, I took videos of myself exploiting the vulnerabilities while speaking slow and simple English. With this, I also sent links in the native language and in some better ones in English.
After reporting the findings, the token was being validated on the backend and the proper entropy was added to the reset tokens. However, because of the lack of resources (and difficulty) for the security patches, the Host Header Injection took 4 attempts to try to fix. The owner of the site said they would not pay until the issue was fixed. Magically, the next day, the vulnerability was patched! So, I'm unsure if they did not care about patching the bug or if they just did not know how. Regardless, this makes me consider the security of websites outside of the normal public eye and whether or non-English speakers have the proper resources to make secure software.
Password reset functionality is one of the hardest aspects of the user experience to get right. When something does go wrong with this, an account compromise can occur. Below are a list of issues with the password reset functionality found during this security review:
- Username/email enumeration
- Host Header Injection
- Insufficient Length of Reset Tokens
- Lack of Validation of Magic Values
- Extended Expiration Time on Token
- Lack of Brute Force Protections
Besides the issues described above, there are a few more that should be tested for:
- Sending New Passwords over Email
- Lack of Two Factor Authentication
- Multiple Uses for a Single Reset Token
- Reset tokens Not Specific To User
This is a fairly good list for testing password reset functionality. But, the test cases differ depending on the implementation.
Defense In Depth
Some of the issues listed above do not lead to a direct compromise of the user account but are considered best practice. By having a defense-in-depth mindset, subtle issues become very difficult or impossible to exploit.
For instance, the insufficient token length vulnerability was made possible to exploit by a lack of brute force protections and extended length of the tokens times. If the brute forcing was not possible, this vulnerability would have been very difficult to exploit.
Overall, adding slight security optimizations makes the overall security of the site much better. In the case that a single issue is found, having defense-in-depth measures can make the vulnerability more difficult to exploit for an attacker. During assessments, it is extremely important to point out not just impactful vulnerabilities but small/subtle things to help out the overall security of the site.
Security is hard to get right! This is why having hackers poke around is a good thing (of course, get permission first). I hope you enjoyed the article and learned something interesting about web applications today. Feel free to reach out to me (contact information is in the footer) if you have any questions or comments about this article. Cheers from Maxwell "ꓘ" Dulin.