Say you have an application on a pentesting gig. Say that application has a special “Recover your account” routine, which includes authenticating with your personal information, such as your Social Security Number (Or at least the last 4 digits), your account number and your date of birth, instead of the usual “We’ll send a link to your email account”. Now, say that because of the application’s password policy, it prevents you from using that information as part of your password. Now, say we use this against them…
This type of authentication, which is done by humans instead of an automated system, is usually left for cases in which the application being accessed has the following characteristics:
- Is part of a big entity (One that can afford having human verification)
- Has highly sensitive data or allows accessing resources which can critically affect people in real life.
- In case of any data or resources being breached, the entity’s reputation could be at great peril, or even suffer heavy financial loss up to and including bankruptcy.
The reasoning behind this, which could be questionable, is that email addresses are easily hacked every day via phishing, malware and/or data leaks.
Now, of course behind all of this, lies a heavy and seemingly secure infrastructure, which includes limited attempts and preventive account lockouts, rate limiting, various layers of hacking attempt protections and of course a strong password policy.
For obvious reasons, the application on this gig, prevented the user from using data which could then be used to authenticate it, as part of the account password. This means that you could not use your SSN, your account number or your date of birth. None of that information could be used as part of the application’s user name as well (Although who would use their SSN as part of their username).
The application also had a check against the last 5 passwords used, including the current one. This would mean that if that the user were to change its password, either because it was requested by the user or because it was requested the application, the application would not allow the user to set the same password it currently has.
Since it would be insane to make this check as a client side script because it would kinda look like this:
So, this application had to send a request each time you pressed a key, using XMLHTTPRequest, to a special endpoint which responded with the strength of the password sent, and if it was a valid password at all.
Now, of course I cannot show the actual requests to the application, but I’ve roughly replicated them on a local application so that it could be seen more clearly:
After seeing this, some of you may have noticed the same thing I noticed, and got hacker’s goosebumps. It seems that our developer friends, instead of taking the current logged in user from the session context, sent it as a user-controlledparameter. This was easily checked by trying with my old password and then trying with my old password and a different username:
Changing the user name to one different than my own:
Needless to say, that should not happen. The server should take the username against which the test is being done from the session context, not from a user-controlled parameter.
What can we do with this? The first thing that comes to mind is that we can brute-force a user’s password with unlimited tries, on an endpoint that is surely not being monitored and expects lots of traffic (each time a user types a letter on the password box, a request is sent). But we can also go another way…
We said that in order to authenticate us against a human, thus getting access to an account we need:
- The last 4 digits of the user’s SSN.
- The user’s date of birth.
- The user’s account number.
We can brute force these values from the endpoint, which would take a lot less time and would have a 100% success rate. For this, we can use Burp’s intruder:
And we let it run. After a few minutes:
Rinse and repeat for DOB, and account number and you’re done. Of course, you would need to put your best poker face, and go the social engineering way to gain actual access to a target’s account, but that’s another story…
Conclusion: I guess the classic recommendations stand here as well as anywhere. Never trust user-controlled data and always extract the user against which a check or a change is made from the session context. And for the pentesters: Never underestimate application’s endpoints and don’t just rely on Burp’s active scan 😀