I spoofed access to other people’s email in order to pre-steal user accounts before they are first registered. Here’s how I did it.
One thing I always test while hacking on bug bounty programs is how applications generate tokens. Tokens are used for things such as password resets, email address verification, one-click sign-in, etc.
While hacking on one private program I discovered an application with a weak algorithm for generating email verification tokens. This is how I approached the problem and how I found a way to generate valid tokens to verify ownership of any email address, even those I didn’t actually have access to. This enabled me to pre-register accounts on any email address with a password of my choice which leads to an account takeover when the real owner eventually registers.
Analysing The Token Generation Algorithm
The first thing I test while attacking any web application with a bug bounty scope is the account creation and authentication functionality. On this occasion, I discovered that each time I created a new user account, the application wouldn’t let me do anything until I had verified ownership of my email address. The target application would email me a verification link and prompt me to check my inbox and that was as far as I could go
By forcing me to validate my email address the application owners can prevent pre-account takeovers.
Modern web applications often have multiple ways to log in to a user account. These can include:
- Username and password
- Email address and password
- Oauth such as ‘Sign in with Facebook’ or ‘Login with Twitter’
- SAML Single Sign-On from any SAML compatible Identity Provider (IdP)
- Passwordless using tokenised ‘magic’ links
A pre-account takeover is when an attacker creates a user account with one login method and then the victim creates another account with another login method. The application then links the two accounts together based on the matching email address. When the email address is not validated, or validations are bypassed, this can lead to pre or post account creation takeovers.
The attack happens as follows:
- The attacker creates a user account on a web application using an email address that they don’t own.
- The application doesn’t verify that they are the owner of the email address
- The account sits dormant for a period of time
- The real owner of the email address chooses to ‘sign-in with Facebook’ and Facebook provides the users validated email address.
- The Facebook email address matches the email address the attacker signed up with and the two accounts are linked.
- Now the attacker can log in to the victim’s account with an email address and password that they supplied and they can see everything the victim does inside their account each time they log in with Facebook.
Email Verification Token Recon
Checking my inbox, I found the ‘confirm your account’ email and copied the link into my favourite text editor. I always save all one-time links emailed to me along with the account name, email address, registration time, and so on, in case any patterns emerge while testing.
The link had the format https://craighays.com/verify/?P1=randomString &P2=randomString &P3=staticText &P4=staticText (without the spaces).
After generating a few verification emails for different accounts I could see that P3 and P4 were always the same things. P1 and P2, however, always changed each time an email was sent. Opening the link after removing or altering P1 or P2 resulted in an error message and the email address was not verified. Therefore, the combination of P1 and P2 together formed a two-part email address ownership verification token.
Now that I had identified the two-part token used to confirm an email address, I took a deeper look at the two parameters.
Both P1 and P2 were base64 encoded strings. An easy way to spot this is when a random-looking string ends with a single = or double ==. These = chars are used as padding to ensure the pre-encoded string meets the minimum bit length. This isn’t always the case though as a perfect-length string won’t need padding. If in doubt, always pass random strings through different decoders to see if anything interesting comes out.
After decoding multiple P1 and P2 parameters from different links I could see that P1 always had the format of four static characters followed by 12 numerical characters such as TEXT000123456789 and P2 always had the format of 9 numerical characters such as 123456789. Looking through my list of decoded P1 and P2 strings, I could see that the numbers were incrementing, going up in large steps from one email verification link to another. P1 and P2 were not correlated in any way other than that they both went up.
At first, I tried to guess the numbered strings based on the timestamps in the HTTP response, but that was getting me nowhere. Instead, I had the idea of defining upper and lower bounds within which a valid token for an email address would reside. With a small range, even a 9 or 12 digit number could be brute-forced to create a valid token.
Upper and Lower Bounds
Looking at the problem mathematically, I needed to guess two 9 digit numbers. The twelve-digit number always started with 000 so it was effectively 9 digits long. A 9 digit number has 1,000,000,000 possible combinations. Since I have two of them, I have 1,000,000,000 * 1,000,000,000 possible combinations for each email verification token. Pretty secure.
The issue with this application was that in each token generated, P1 and P2 had numbers greater than the previous P1 and P2, but lower than the next P1 and P2. If I can create a token that I can see, followed by one I can’t, then a third that I can see, I can reduce the range of possible numbers significantly. I can create upper and lower bounds on the 9 digit number which is a much smaller range than the 1,000,000,000 possible combinations.
The step by step of my token attack looked like this:
- Create an account with an email address I own
- Create an account with the email address I don’t own, but want to validate
- Create an account with an email address I own
Putting This Into Practice
In order to prove that I could validate any email address, I decided to validate my own name at the target company domain, e.g. [email protected][Read more…] about Pre-Account Takeover by Reversing a Weak Email Verification Token Algorithm