Hello ethical hackers! Welcome to this new episode of the OWASP Top 10 vulnerabilities series. In this article, you will learn Cross-Site Scripting (XSS).
I’ve prepared a free practical testing lab VM which contains the best vulnerable web applications. The best approach to learn hacking is practice!
There is so much content addressing this subject, from discovery to all kinds of filter bypass. This blog post will explain all these aspects and give you references to go deeper for each one. Hopefully, it will be a general guide for you to come back to when you need anything related to XSS.
During this episode, you will learn the following:
- How does this vulnerability work? This is where you will understand the underlying concepts which allow for a Cross-Site Scripting vulnerability to happen. You can’t understand the rest if you don’t understand this section.
- What are the types of XSS? We will explore all the different types with examples.
- Where to find it? I will share with you the different injection contexts where XSS might occur.
- How to test for it? In this section, you will learn the different approaches to testing for this vulnerability.
- Filter bypass: You will learn how to bypass a Cross-Site Scripting filter bypass in a challenge.
- Some attack examples: You will find and analyze real bugs disclosed on Hackerone. Hopefully, this will inspire you to find them yourself.
- What is the impact? Once you have found a Cross-Site Scripting vulnerability, you will learn what you can do with it.
- How to prevent it? If you are a developer and want to secure your code against this issue, or if you are a bug bounty hunter trying to write an exhaustive report including how to mitigate it, this section is for you.
- References for further reading: There are many awesome in-depth references which will help you go even deeper in particular aspects of this topic.
How does XSS work?
- Producing an interactive user experience with animations, transitions, etc.
- Making API calls to the back-end server.
Web Applications also process user inputs and show the result in the rendered HTML. For example, when you write a comment, the application stores it and shows it in the comments section.
Types of Cross-Site Scripting
There are three types of Cross-Site Scripting. Each one has its own use cases.
Reflected Cross-Site Scripting
As the name suggests, this type happens when the backend reflects untrusted user input into the HTML result page. For example, you navigate to
vulnerable.site/search?query=example. Then, the backend fetches the data from a datastore and returns the result
<h1>example book</h1>. An attacker can insert
alert function. Therefore, the full malicious URL would be:
vulnerable.site/search?query=<code></h1><script>alert('XSS')</script><h1></code>. Consequently, a popup appears in the result HTML page.
In order to target a victim, the attacker must entice him/her to click on the malicious URL so that the popup triggers on his/her Web Browser.
Stored Cross-Site Scripting
In a Stored XSS scenario, the malicious payload we saw earlier gets stored in the database. Let’s consider a comment feature where users comment on an article and list all the comments. An attacker injects
<script>alert(123)</script>. Therefore, any victim which navigates to the comments page will see a popup in his/her Web Browser.
Note that the attacker doesn’t necessarily need to share the link with the victim.
DOM Cross-Site Scripting
<script> var path = document.location.hash.substring(1); window.location = path; </script>
An attacker can trigger the vulnerability using the following URL:
Note: When you find a Cross-Site Scripting vulnerability which cannot exploit other users, it is called a Self-XSS. For example, you can’t target other users with your shipping address shown in your private profile. Therefore, you need to chain it with another vulnerability, like CSRF, to prove a concrete impact.
Now that you understand how XSS works, let’s explore where to find it.
Where to find XSS?
You can find it in many contexts, depending on where your input gets inserted. The following are some use cases. In all the injection contexts, the general idea is to construct a valid HTML piece of code which will trigger your XSS.
Inject in HTML tags
When you notice your user input inside HTML tags, you have to test if you can inject arbitrary tags. For example, let’s suppose that the following endpoint
<h1>Results for hacking are …</h1>. You can replace hacking with
<img>. If you see a broken image in the result, this is a strong indication that you can achieve an Cross-site Scripting.
Inject in HTML attributes
When you notice your user input inside HTML attributes, you have to test if you can inject arbitrary attributes or escape from the context of the attribute. For example, let’s suppose that the following endpoint
<h1 id="hacking">Results are …</h1>. You can replace hacking with
dummy style="color:red". If you see a the text
Results are … in red, this is a strong indication that you can achieve an XSS. Besides, you can also inject
dummy"><img src=x><h1 and see whether you can inject an image. If you succeed, this indicates that you can escape the context of the attribute and potentially inject arbitrary tags, falling back to injection in the HTML tags context.
<h1>var query = "hacking"<h1>. In this case, try to inject
hacking";console.log("potential XSS");//. If you see the message
potential XSS in the Web Browser’s console, this means that you have achieved an XSS.
Inject in a JSON response
Sometimes, an API returns a JSON response with a Content-Type HTTP header of
text/html. If this happens, try to inject a HTML tag like
<img> and see if your Web Browser gets a broken image.
Because XSS can trigger in many injection contexts, you can use Cross-Site Scripting polyglots which are designed to cover as many contexts as possible. For example, have a look at this polyglot which targets many injection contexts.
How to test for XSS?
There are many approaches you can follow to hunt and test for XSS.
Manual, error-based testing
This is the most basic approach. Basically, you inject a payload in all the fields that you find. Whenever an XSS triggers, you will see a popup. Although you can find this vulnerability with this basic technique, it is a tedious task. Besides, there are some cases where you will not see a popup. For example, the XSS can trigger in a separate application run by an agent.
Manual, blind-based testing
In order to increase your chance of finding XSS, you can use a blind approach. Basically, instead of relying on a popup as proof, you can inject a callback to a server which you control. For example, you can inject
<img src=”http://malicious-server”>. When an XSS triggers, you will get a callback to your server.
Automated approach using XSS testing tools
This is where automated scanners come into play. For example, you can use Burp Suite Pro or OWASP ZAP to test for Cross-Site Scripting vulnerabilities. As we demonstrated in the SQL injection hands-on tutorial, both of them allow you to target specific vulnerabilities. Besides, there is a rising tool, currently on beta, called KNOXSS which specializes in finding reflected and DOM XSS at the moment.
XSS impact, beyond alert
alert function to prove the existence of XSS. This is more than enough for the majority of your clients. However, you can go beyond that when your client gives you permission.
When you succeed at exploiting a Cross-site Scripting vulnerability, it’s like you’ve got a chair in front of the victim’s Web Browser. You can perform almost all the operations the user can do on the vulnerable application.
Steal cookies and sensitive data
The code above inserts an image with id
xss in the vulnerable page. Then, it sets its
src attribute to point to your attacking website while appending the victim’s cookie. When the victim loads the page, you will get the cookie value as part of the callback URL which you receive in your attacking server.
Redirect to a malicious website
Deface a website
Another way to exploit XSS is to deface the vulnerable page itself. This technique is usually used by hacktivists to harm the image of the target.
XSS filter bypass
You might think that the best approach to prevent this issue would be to sanitize user inputs. Unfortunately, this is not the case. In fact, hackers always find bypasses to XSS filters. OWASP provides the XSS filter evasion cheat sheet which hackers typically use for this purpose.
In the video tutorial at the end of this article, you can see how a poorly written filter doesn’t prevent XSS.
Cross-Site Scripting prevention
The basic idea to prevent XSS is to tell the Web Browser how to differentiate between HTML and the data. You do that by properly encoding the data. For example, you can perform HTML entity encoding to transform the malicious user input
<img>. When the browser sees that encoded string, it doesn’t consider an image tag.
Of course, you need to take into consideration each injection context. That’s why you should use an encoding library. For example, OWASP Encoder allows you to properly encode user inputs to prevent XSS in Java.
XSS attack examples
Let’s start with a reflected XSS. This report demonstrates how you can redirect the victim to an arbitrary location.
In this report, the hacker stores a payload in the name of a resource. When the victim loads it, the Stored XSS triggers.
This report is an example of a DOM XSS. I chose it because Hackerone uses Content Security Policy (CSP), which makes it hard to exploit XSS. However, it is not impossible.
Finally, this report demonstrates how you should never trust a WAF to totally protect you against attackers. While it prevents the majority of them, determined hackers will always find a way to bypass XSS filters.
XSS references and cheat sheets
XSS is a big topic and I can’t include everything in detail in one post. I’ve tried to make it as exhaustive as I can. So, here is a list of references which you can explore when you want to dig deeper into Cross-Site Scripting.
- XSS payloads in GitHub repositories: There are many repositories for this purpose, this one is exhaustive. If you want a text file to use in your fuzzing, you can use this one.
- Britelogic’s webGun payload builder: An interactive XSS payload builder to help you find the payload which fits your needs.
- Portswigger’s XSS cheat sheet: Provides both interactive and PDF resources which help you find the best payload to use for a specific injection context. It will help you to find a filter bypass as well.
- OWASP XSS prevention cheat sheet: An in-depth overview of the different prevention rules to mitigate XSS.
That was it! I hope you enjoyed reading this article and learned something new. If you did, share the knowledge with your network! Until then, stay curious, learn new things and go find some bugs.
Here is your XSS video tutorial.