How to Find XSS | HackerOne


What Is XSS?

XSS, short for Cross-Site Scripting, is a common type of vulnerability in web applications that executes arbitrary JavaScript in the victim’s browser. XSS can often be chained with other vulnerabilities to mount more impactful attacks, such as information disclosure, account takeover, and even remote code execution.

XSS Vulnerabilities and How to Find Them

XSS vulnerabilities discovered by security researchers can be grouped into three general categories: reflected, stored, and DOM-based, but other interesting situations also crop up (like blind XSS and server-side XSS). In this article, we will introduce each type of XSS and share tips and tricks on how to look for them.

Payloads to Use

For effective testing of parameters that might end up executing JavaScript, polyglots (a piece of data that can be interpreted into different formats) are extremely useful, as are large lists of known XSS payloads that might work in different scenarios. 

For example, a straight-up 

 " onclick=alert(1)//

The PayloadsAllTheThings GitHub repo has many normal and polyglot XSS payloads in different scenarios. The HackTricks page on XSS describes many scenarios and corresponding payloads. Last but not least, this Auto_Wordlists repo has many XSS wordlists for automated testing.

Manual vs. Automated

Both manual and automated approaches to finding XSS vulnerabilities have advantages. One consideration is scope: if the scope of a pentest or bug bounty program is very large in terms of apps and subdomains, employing an automated approach would help to find low-hanging XSS bugs in a large number of assets.

Popular tools for automating finding XSS bugs include Dalfox, XSStrike, and callback platforms like xsshunter (depending on the policy of the program you are hunting on, as not all programs allow third party XSS callbacks). Dalfox supports both stored and reflected XSS, whereas XSStrike only supports reflected; xsshunter acts as a self-hosted callback XSS hook, which can be used in combination with Dalfox for blind XSS testing (where the JavaScript could be executed but is not immediately apparent in the response). At the moment, automated tools for finding anything beyond low-hanging reflected XSS are limited; they are best used to augment manual testing.

Manual testing is the only way to find deeper XSS vulnerabilities which may require bypassing complex application filters, different encodings, and bypassing Web Application Firewalls (WAFs). During manual testing of an application, you naturally pick up interesting application flows and parameters to test XSS with (such as OAuth login redirect parameters, user input being embedded in background API calls, and so on).

Reflected XSS

The most straightforward type of XSS vulnerability is reflected XSS (or RXSS for short). This is a type of non-persistent XSS (the attack payload does not persist on the server) that reflects the user input in an unsanitized way back to the output web page, resulting in the embedding of user-supplied HTML elements or attributes that are executed by the browser.

RXSS is usually found in a GET request where parameters in the URL are reflected back to the browser without proper encoding. Good examples of these include search queries, redirects, and error messages.

For example, in this report, the error message on a login page is reflected from the URL without being HTML encoded:

 

Another very popular place that RXSS pops up (pun intended) is redirect parameters. Application endpoints such as post-login redirects, "confirm you are leaving this site" redirects, and so on often contain parameters in the URL that the browser interprets; however, unchecked special URLs, such as ones starting with the "javascript" protocol, can lead to JavaScript execution.

For example, this bug report describes an RXSS in a Shopify subdomain in the "returnTo" parameter, which appears to be a parameter that defines the redirect URL after a new account is created:

Reflected XSS report

 

Stored XSS

Stored XSS refers to user input stored by the web application that is unsanitized when rendered. Any web application that stores data from users (or other external sources) and then displays it elsewhere could be vulnerable to stored XSS. Popular places to look for stored XSS are comment fields, user profile data, private messages, and even emails. A famous example of stored XSS is the Samy Worm, which was a self-propagating XSS payload that added Samy Kamkar as a friend and posted a status update:  "but most of all, samy is my hero".

To find stored XSS, you need to find a relationship between the source and the sink, i.e. you need to find where the user input eventually gets printed. Sometimes it's obvious, such as in the comment section of a blog. Other times, it's available on a separate page, like the user's profile after an update of the bio.

Sometimes, stored XSS is found in the lack of sanitization when converting from one format to another, such as rendering Markdown to HTML, like in this report on GitLab Stored XSS in its wiki:

 

Stored XSS report

 

This is also a good example of mutation XSS, where broken or invalid HTML syntax is automatically fixed by the browser to facilitate a complex filter bypass. In places where free-form HTML code can be used in a limited way, like Markdown input, special syntax in forum posts, and so on, you may be able to find stored XSS by diving deep and playing around with different filter bypasses. For a really in-depth guide to bypassing XSS defenses (mutation XSS), check out chapter 2 of the "Beyond XSS" book.

Blind XSS

Blind XSS (bXSS) is a form of stored XSS that is executed blindly in the sense that the payload is rendered on a system not accessible to the attacker. This often takes place in some administrative backend. The attacker can abuse the blind XSS to exfiltrate information or perform actions that would not otherwise be visible to them. 

Some common places to find bXSS include:

  • Injecting into logs via HTTP headers (like user agent and cookies)
  • Account registration
  • Feedback forms
  • Usernames / Email addresses

Blind XSS vulnerabilities can be tricky to exploit because the researcher can't see the payload trigger in their browser. Instead, the attacker needs to host a callback endpoint (such as xsshunter or Burp collaborator) to detect the payload execution. A more complex payload can be injected to exfiltrate the contents of the current page. 

For example, in this report the hacker put an XSS payload into the user's name during registration time, and the admin dashboard in a third-party domain triggers the XSS payload. To maximize your chances of finding Blind XSS on a program, make sure to include XSS payloads that do remote callbacks and have a persistent callback service that alerts you when payloads are triggered.

Blind XSS report

DOM-based XSS

The Document Object Model (DOM for short) is the internal representation of parsed HTML code in the browser, which gets rendered and displayed as the resulting web page. This document object can be manipulated with JavaScript to dynamically change the contents of a page; in fact, it is often done by Single Page Applications (SPAs) and JavaScript frameworks to dynamically update a web application with new content.

DOM-based XSS refers to an XSS that takes place purely in the client-side code, such as a call to the document.write function or appending data to the innerHTML of an element. Finding DOM-based XSS often requires analysis of sources and sinks in the JavaScript code. A good tool for doing that is Burp Suite's DOM Invader, which is installed in the Burps' default browser as an extension. To enable it, go into extensions in the Burp browser and turn on "Burp Suite":

Chrome extension for Burp Suite

 

Then, click on the enabled extension on the top right corner, enable everything, and copy the canary it generates: 

DOM invader

Now, when DOM Invader loads a web page, you can use that canary in the fields you want to test (e.g. search fields, and it will highlight the sinks that it has identified the canary going into:

DOM invader on a page

Here, DOMInvader has identified a DOM XSS vulnerability in Portswigger Academy's Gin and Juice Shop (an intentionally vulnerable web application). It highlights the part of the payload that's embedded in the sink, and from there, we can craft an exploit, knowing that the user input is embedded in the end of a src attribute in an img tag:

How to Find XSS | HackerOne

We can click the "Exploit" button, at which point DOM invader tries to close off the tag and add a new HTML tag to execute JavaScript:

https://ginandjuice.shop/blog/?search=%22%27%3E%3Cimg%20src%20onerror=alert(1)%3E&back=1

Which essentially manipulates the element to this:

How to Find XSS | HackerOneHow to Find XSS | HackerOne">

But that doesn't work, as HTML tags are not allowed, and the web app blocks our request:

DOMInvader request blocked

 

But since we now know where the vulnerability is, we can create our own payload, which just adds an onload attribute to execute JavaScript after the image is loaded:

How to Find XSS | HackerOne

DOMInvader onload attribute

 

And just like that, we've found and validated a DOM-based XSS using DOM invader.

Finding XSS in Unusual Places

XSS in PDFs

Pay extra attention to any functionality that generates PDFs, as the rendering process of a HTML page into PDF could result in the execution of JavaScript in the process on the server side, leading to Server Side XSS and other bugs like local file inclusion and SSRF via iframe and other tags. Server Side XSS has a severe impact and can be used to disclose information about the server, use it to request data from internal servers, and could even be used to run an internal port scan.

You can use some basic tags such as How to Find XSS | HackerOne to test if JavaScript is executed during the PDF generation process to add extra data into the document, and then try other things like loading a remote script from your own domain (like