Brute Forcing Paswords with CSRF

CSRF is a really powerful tool. At my recent talk at Derbycon I talked about how CSRF is a big problem for startups who build their product in PHP.

Quickly, to give a 1 minute explanation of Cross-site Request Forgery (CSRF). CSRF is a common web vulnerability where a malicious site embeds client-side code that makes a request to a different site (hence cross-site). This request is meant to change 'state' and perform some action on behalf of the victim who navigated to the malicious site. Here is what the flow of CSRF looks like.

One interesting thing that came from my startup centered research is the realization that the absence of a token for an email or password update does not mean it is feasible to perform an account hijack. A "secure flow" that requires the user to enter their current password to perform account updates is a slight improvement but is insufficient to prevent account hijacking.

What is important to note is that reprompting the user for a password is still vulnerable to dictionary attacks through CSRF. Below is an example of this type of form submission.

Usually when an attacker uses a CSRF vulnerability that requires a post request, the evil page will have code that looks like this.

<iframe src="requestpage.php">

Having a separate page make the post request within the iframe is preferable because form submissions forward the page to the URL that is being submitted. Within this separate iframed page, the attacker will have a form that auto-submits the post request that attempts to update the user's password. It might look something like this.

<form name="bigform" action="/" method="post">
<input name="oldemail" value="[email protected]">
<input name="newemail" value="[email protected]">
<input name="password" type=password value="password">

If an attacker wishes to actually use this csrf they have two options. The first option is to make one guess, but this will have a really low success rate. A better option is to use a password dictionary and try a lot of different passwords. This second option requires a little bit extra work.

To actually perform the dictionary attack, first we need a password dictionary. With some quick VIMming I changed the famous rockyou password dictionary into a giant PHP array. I added this PHP array to the page I want victims to navigate to. I then iterate through this array and output code that does two things.

First, this code outputs javascript that adds iframes dynamically to the page for each index in my rockyou array. It's kind of ugly and there are other ways of doing this (like outputting a javascript array of all passwords), but it works.

Second, it loads these iframes with a setTimeout delay, to prevent your browser from attempting to do too much at once. I have noticed a lot of performance issues when making too many requests at once (the browser won't do them all at once, but things still slow down).

The end result of these looks something like this:

Doing this allows you to get a lot of chances to correctly guess a user's password when sites require the old password to modify an account but doesn't include a CSRF token in form submissions. This dictionary attack, with the code in the above image, attempts 2 passwords per second. This isn't great, but it greatly increases your chances of guessing the victim's password. There is one final benefit. Many times, there is no throttle to this type of attack for two reasons. First, the user is already authenticated. Second, because if they don't have CSRF tokens...they probably won't have a throttle either.

ejj, Sept 2014