As I continue my love-hate relationship with XSS, I am slowly but surely building a better understanding of more advanced XSS techniques. I have always known the basics of DOM XSS but never really dug deeper into it until recently. There are a lot of good XSS scanner tools out there such as XSSStrike, but running just tools is no fun unless you understand the underlying techniques they are using.

In this blog, I'd like to share how you can find DOM XSS without tools — with just 8 hours of time to kill (lol).

First, we need to understand the basics. For an XSS vulnerability to be exploited, you need to have some sort of input source that you can control. Some common ones include:

location.href, location.search, document.referrer, location.hash, etc.

The second thing you need to be aware of is that for DOM XSS to exist, there must be a dangerous function or property that can execute HTML or JavaScript payloads. These are called "sinks." You can think of some of the following:

.innerHTML, document.write(), eval(), Function(), .action, etc.

DOM XSS only happens if YOUR source (input you control) reaches the sink.

Finding your sink is pretty simple. Armed with a web browser and DevTools, you can start filtering for keywords such as the ones above. When you find multiple lines that contain those sinks, mark each one that may have dynamic data written to it.

The next step is to reverse-trace which data/source reaches the sink. For example, if you found something like this in your search:

element.innerHTML = vulnVAR;

We would need to ask ourselves, where does the vulnVar come from? CTRL+F for the win and identify if you can control the value of vulnVar. By searching the following value, you may be able to find the source of vulnVar and what value it is being assigned with:

var someVar = 

As you navigate through the tedious lines of codes, keep notes if any of the source (location, referrer, user input, etc.) is used to assign the value of someVar. You need to also read how the values are assigned and see if any escaping or sanitization functions are implemented to secure the sink. If not, this could indicate a potential XSS avenue. Once you suspect that there is a source to sink flow, you can check for reflection.

Next we would have to check if your input characters are reflected in the browser. As an example, you supply an input to the search query of a site

victim.com/search=11aa11

Once the page is loaded, you can now open elements tab "F12" and search for your marker "11aa11" inside the DOM. If it is reflected, you will know that DOM XSS is most likely be there. The next thing for you to do is to develop a payload such as

<img src=x onerror=alert()>

If it renders, then DOM XSS is present in the application. Keep in mind this is a simple example, and for more advanced filtering/WAF in place you may need to implement some obfuscation/escape/cool techniques to get your payload to render. Sometimes you'll see suspicious HTML, but it's passed through a sanitizer first — which makes everything more complicated for us.

A few considerations when you are hunting:

  • Network traces: If you don't find reflection in the DOM, go to Network tab, and search for your identifier "11aa11". If you see any requests (GET or POST) containing 11aa11 in the URL, query string, request body, or headers, then the page sent your marker somewhere. Check its destination, if it is sent to a tracker — that just means it's being sent for analytics, not reflected on the site itself.
  • WAF needs to be bypassed

Summary checklist for your guidance to hunt for DOM XSS:

  • Search for sinks (innerHTML, eval, etc.).
  • Trace back variable sources.
  • Inject markers in URL/hash/referrer.
  • Look for reflection in DOM.
  • Use breakpoints to inspect runtime data flow.
  • Test safe payloads.
  • Confirm JS execution.