How does DOM XSS occur?
Example 1: Here’s an actual piece of code I have seen before illustrating this point:
This will not work. This is because although we managed to create a script tag, it wasn’t appended to the DOM yet. Let’s try something a little less obvious:
Example 2: A similar real-world example, but no external libraries this time:
The DOM XSS vulnerability occurs with the .innerHTML. In this context, “parameters” is an object constructed from the GET request. What’s special about this example is that the GET parameters were completely sanitized on the server-side - all HTML stripped and casted into integers. The catch here was that the same sanitization did not occur on the client-side. Because the minimum and maximum values were taken from the querystring and used blindly, we could insert our own HTML, and it would have been inserted into the DOM. By looking at the server-side code alone, we would have never known.
Finding DOM XSS
When looking at DOM XSS vulnerabilities, there is usually a list of recurring features. Some of these include functions being used improperly, or browser-supplied data that is assumed to be cleaned. I’ve assembled a list of things to watch out for, which can make static analysis a little easier.
- document.URL *
- document.location.pathname *
- document.location.href *
- document.location.search *
- document.referrer *
Most of the document.* variables can be accessed through the window object as well. The * indicates the value is urlencoded; the script usually must urldecode these values before a vulnerability can be introduced.
Insecure creation/modification of html elements
- Modifying .innerHTML, .outerHTML
- jQuery’s selector, $()
- jQuery’s .html() function
Functions which evaluate strings as code
- Anything that modifies src attributes of <script> tags
- Anything that modifies event handlers
Like with server-side code, anything that executes code from user input is a big red flag, and is worth investigating.
Why DOM XSS can be more dangerous than traditional XSS
- This gives more flexibility in avoiding detection from a WAF, etc. For example, a common location for DOM XSS payloads is the fragment identifier (everything after # in a URL). This is a good spot for a payload, as the fragment identifier is never sent to the server.
- Anti-XSS filters built into browsers become ineffective.
- More room for vulnerabilities.
- It has to work.