hack bank and casino
A Cross-site request forgery attack, also known as CSRF or XSRF (pronounced sea-surf) is the less well known, but equally dangerous, cousin of the Cross Site Scripting (XSS) attack. Yeah, they come from a rough family.
CSRF is a form of confused deputy attack. Imagine you’re a malcontent who wants to harm another person in a maximum security jail. You’re probably going to have a tough time reaching that person due to your lack of proper credentials. A potentially easier approach to accomplish your misdeed is to confuse a deputy to misuse his authority to commit the dastardly act on your behalf. That’s a much more effective strategy for causing mayhem!
In the case of a CSRF attack, the confused deputy is your browser. After logging into a typical website, the website will issue your browser an authentication token within a cookie. Each subsequent request to sends the cookie back to the site to let the site know that you are authorized to take whatever action you’re taking.
Suppose you visit a malicious website soon after visiting your bank website. Your session on the previous site might still be valid (though most bank websites guard against this carefully). Thus, visiting a carefully crafted malicious website (perhaps you clicked on a spam link) could cause a form post to the previous website. Your browser would send the authentication cookie back to that site and appear to be making a request on your behalf, even though you did not intend to do so.
Let’s take a look at a concrete example to make this clear. This example is the same one I demonstrated as part of my ASP.NET MVC Ninjas on Fire Black Belt Tips talk at Mix in Las Vegas. Feel free to download the source for this sample and follow along.
Here’s a simple banking website I wrote. If your banking site looks like this one, I recommend running away.
The site properly blocks anonymous users from taking any action. You can see that in the code for the controller:
[Authorize] public class HomeController : Controller { //... }
Notice that we use the
After logging in, we get a simple form that allows us to transfer
money to another account in the bank. Note that for the sake of the
demo, I’ve included an information disclosure vulnerability by allowing
you to see the balance for other bank members. ;)AuthorizeAttribute
on the controller (without specifying any roles) to specify that all
actions of this controller require the user to be authentication.To transfer money to my Bookie, for example, I can enter an amount of $1000, select the Bookie account, and then click Transfer. The following shows the HTTP POST that is sent to the website (slightly edited for brevity):
There are three important things to notice here. We are posting to a well known URL, /Home/Transfer, we are sending a cookie,POST /Home/Transfer HTTP/1.1 Referer: http://localhost:54607/csrf-mvc.html User-Agent: ... Content-Type: application/x-www-form-urlencoded Host: 127.0.0.1:54607 Content-Length: 34 Cookie: .ASPXAUTH=98A250...03BB37 Amount=1000&destinationAccountId=3
.ASPXAUTH
, which lets the site know we are already logged in, and we are posting some data (Amount=1000&destinationAccountId=3
),
namely the amount we want to transfer and the account id we want to
transfer to. Let’s briefly look at the code that executes the transfer.[AcceptVerbs(HttpVerbs.Post)] public ActionResult Transfer(int destinationAccountId, double amount) { string username = User.Identity.Name; Account source = _context.Accounts.First(a => a.Username == username); Account destination = _context.Accounts.FirstOrDefault( a => a.Id == destinationAccountId); source.Balance -= amount; destination.Balance += amount; _context.SubmitChanges(); return RedirectToAction("Index"); }
Disclaimer: Do not write code like this.
This code is for demonstration purposes only. For example, I don’t
ensure that amount non-negative, which means you can enter a negative
value to transfer money from another account. Like I said, if you see a
bank website like this, run!
The code is straightforward. We simply transfer money from one
account to another. At this point, everything looks fine. We’re making
sure the user is logged in before we transfer money. And we are making
sure that this method can only be called from a POST request and not a
GET request (this last point is important. Never allow changes to data via a GET request).So what could go wrong?
Well BadGuy, another bank user has an idea. He sets up a website that has a page with the following code:
<html> <head> <title></title> </head> <body> <form name="badform" method="post" action="http://localhost:54607/Home/Transfer"> <input type="hidden" name="destinationAccountId" value="2" /> <input type="hidden" name="amount" value="1000" /> </form> <script type="text/javascript"> document.badform.submit(); </script> </body> </html>
What he’s done here is create an HTML page that
replicates the fields in bank transfer form as hidden inputs and then
runs some JavaScript to submit the form. The form has its
When you visit this page it makes a form post back to the bank site. If you want to try this out, I am hosting this HTML here. You have to make sure the website sample code is running on your machine before you click that link to see it working.action
set to post to the bank’s URL.Let’s look at the contents of that form post.
It looks exactly the same as the one before, except thePOST /Home/Transfer HTTP/1.1 Referer: http://haacked.com/demos/csrf-mvc.html User-Agent: ... Content-Type: application/x-www-form-urlencoded Host: 127.0.0.1:54607 Content-Length: 34 Cookie: .ASPXAUTH=98A250...03BB37 Amount=1000&destinationAccountId=2
Referer
is different. When the unsuspecting bank user visited the bad guy’s
website, it recreated a form post to transfer funds, and the browser
unwittingly sent the still active session cookie containing the user’s
authentication information.The end result is that I’m out of $1000 and BadGuy has his bank account increased by $1000. Drat!
It might seem that you could rely on the checking the
Referer
to prevent this attack, but some proxy servers etc… will strip out the Referer
field in order to maintain privacy. Also, there may be ways to spoof the Referer field. Another mitigation is to constantly change the URL used for performing sensitive operations like this.In general, the standard approach to mitigating CSRF attacks is to render a “canary” in the form (typically a hidden input) that the attacker couldn’t know or compute. When the form is submitted, the server validates that the submitted canary is correct. Now this assumes that the browser is trusted since the point of the attack is to get the general public to misuse their own browser’s authority.
It turns out this is mostly a reasonable assumption since browsers do not allow using
XmlHttp
to make a cross-domain GET request. This makes it difficult for the
attacker to obtain the canary using the current user’s credentials.
However, a bug in an older browser, or in a browser plugin, might allow
alternate means for the bad guy’s site to grab the current user’s
canary.The mitigation in ASP.NET MVC is to use the AntiForgery helpers. Steve Sanderson has a great post detailing their usage.
The first step is to add the
ValidateAntiForgeryTokenAttribute
to the action method. This will validate the “canary”.[ValidateAntiForgeryToken] public ActionResult Transfer(int destinationAccountId, double amount) { ///... code you've already seen ... }
The next step is to add the canary to the form in your view via the
The following shows the relevant section of the view.Html.AntiForgeryToken()
method.<% using (Html.BeginForm("Transfer", "Home")) { %> <p> <label for="Amount">Amount:</legend> <%= Html.TextBox("Amount")%> </p> <p> <label for="destinationAccountId"> Destination Account: </legend> <%= Html.DropDownList("destinationAccountId", "Select an Account") %> </p> <p> <%= Html.AntiForgeryToken() %> <input type="submit" value="transfer" /> </p> <% } %>
When you view source, you’ll see the following hidden input.
<input name="__RequestVerificationToken" type="hidden" value="WaE634+3jjeuJFgcVB7FMKNzOxKrPq/WwQmU7iqD7PxyTtf8H8M3hre+VUZY1Hxf" />
At the same time, we also issue a cookie with that
value encrypted. When the form post is submitted, we compare the cookie
value to the submitted verification token and ensure that they match.
Should you be worried?
The point of this post is not to be alarmist, but to
raise awareness. Most sites will never really have to worry about this
attack in the first place. If your site is not well known or doesn’t
manage valuable resources that can be transferred to others, then it’s
not as likely to be targeted by a mass phishing attack by those looking
to make a buck.
Of course, financial gain is not the only motivation
for a CSRF attack. Some people are just a-holes and like to grief large
popular sites. For example, a bad guy might use this attack to try and
post stories on a popular link aggregator site like Digg.
One point I would like to stress is that it is very
important to never allow any changes to data via GET requests. To
understand why, check out this post as well as this story about the Google Web Accelerator.
What about Web Forms?
It turns out Web Forms are not immune to this attack by default. I have a follow-up post that talks about this and the mitigation.If you missed the link to the sample code before, you can download the source here (compiled against ASP.NET MVC 2).
0 komentar:
Posting Komentar