So, what is DOM XSS?
For over ten years Cross-Site Scripting (XSS) has been included within the OWASP’s Top 10 Web Application Security Risks. Although the issue is extremely well covered online, it is sometimes overlooked by application owners, meaning the vulnerability persists in even some of the most sophisticated modern-day web applications. Despite being heavily documented, as penetration testers, we see this on a common basis taking several forms.
In the modern era of web applications, more responsibility is passed to client-side scripts to deal with processing data. In turn, new techniques have arisen and the threat landscape for such issues is shifting. As mentioned, the coverage of XSS through online outlets is second to none, being one of the most well-covered web application security issues to date. However, it is surprising how many people still do not fully understand the concept surrounding ‘DOM (Document Object Model) Based’ or Client-Side XSS.
Before we go into the details of the issue, there are few key points which must be discussed:
Client Side Vs. Server Side
One key concept that must be understood to fully grasp the issue, is the difference between the terms ‘client side’ and ‘server side’ within this context. This blog does not fully discuss differences between client side and server side functions as a whole, but more specifically code that may provide functionality to the respective sides.
Server side functions are events that happen on the remote server, such as processing, and storing and retrieving data. Here, popular languages such as PHP, C# and more recently Python and GO allow the application to interact with datastores and facilitate more complex functionality.
The key takeaway from this is that client side functions happen within the browser and do not require a change to the HTTP request in order to modify what is seen on the page. Thus, anything which happens within the client side, for example processing a payload, does not depend on the server. This concept is the key to understanding DOM based XSS.
Sources and Sinks
As the names states, DOM based XSS is facilitated and exploited through the use of dangerous client side scripts. The way in which this is illustrated, for the purposes of this issue, is through sources and sinks.
A sink is the output where the value passed from the source is directly injected back into the DOM. This could be a method such as ‘innerHTML’ or ‘document.write’. Again, there are numerous ways in which data could be outputted, potentially presenting a significant opportunity for attacks. However, it is the whole process flow that allows such an attack vector to exist.
The code above takes a search string passed via a GET parameter in the URL. Although the page has to reload in order for this to take effect, the payload is never processed by the server and relies on the client side completely. See the following, which breaks-down the above code to demonstrate the previously mentioned parts:
Window.location.search – this acts as the source. The source of the vulnerability, which is user controllable and passed to the rest of the code.
For instance, if a user passed the value ‘<img src=test onerror=alert(1)>’ through the search parameter, the value would be passed to document.write and an alert would be seen on screen. This is similar to a reflected XSS attack, in the way that unsafe input is directly displayed back onto the users screen. However, as previously mentioned the payload is never processed by the server itself.
As you can see from what is demonstrated above, the concept does not differ from reflected or stored XSS. However, what is key to remember is that the payload is never processed by the server. Although GET requests are processed by the server, the response does not facilitate the attack. However the client side writes the payload to the document executing a similar affect.
As demonstrated in the screenshots, the payload can be seen in the DOM but not in the server response. Thus, classifying such a vector as DOM based.
What does this mean in terms of consequence? As with any other type of XSS, the consequences could be low severity issues to a potentially critical issue. It all depends on circumstance. If the attack vector only affects the current user in page and could not be leveraged by an external attacker, the risk could be considered low severity. However, if the DOM based vector allowed for stealing admin users’ cookies, then this could be considered a high severity vector. The point is, a vector being considered DOM based is no less severe than traditional reflected or stored XSS vulnerabilities.
In terms of remediation, although specifics are not possible as there are a plethora of source and sink combinations, developers should think about how user input is handled. Ask the questions, does a user really need to search for special characters? Does the query string need to be treated as HTML when displayed on the page? It all falls down to circumstance and planning. Before writing a function, all things should be considered, for example, how user input is going to be used. In the case above, if careful planning had been undertaken, the source could be used in a perfectly safe manor with a different sink. If the developer had replaced document.write, with a simple ID selection and text.content statement, unsafe input would be reflected in a safe manner.
As mentioned previously, this issue is one of the most well covered out there. The issue stems from a lack of planning and understanding of how user input may be utilised in a malicious way. The responsibility of dealing with such a task is on developers themselves, not relying on third party libraries or other people’s code to keep them safe. A full understanding of what XSS actually is will help ensure weaknesses cannot be leveraged via utilisation of dangerous user input vectors.
To summarise this whole issue into few sentences, remember the following; the flaw stems from client side code, as such the payload is never processed by the server, making this a difficult concept to understand. However, the key to ensuring your application is safe from such issues is understanding the problem and how a user could potentially escape intended functionality to cause harm. In turn, allowing pre-emptive techniques to be put into place, dealing with any potentially dangerous payloads.