In an earlier blog post we looked at a basic introduction to Cross-Site Scripting or “XSS”, and how to prevent it when developing in-house web applications.
In that article we mainly focused on outlining how a basic form of XSS known as “Reflected XSS” works, because this is the simplest form to explain, as well as the form that is most commonly encountered, and the simplest for an attacker to exploit.
We also mentioned a variant known as “DOM XSS” in that article but didn’t dig further into exactly how it worked – that’s what we’re going to look at today. Strictly speaking, this form of XSS is more broadly referred to as “Client-Side XSS”.
“DOM” stands for “Document Object Model” and although you may not have heard of it, it underpins how every web browser interprets and renders web pages that are received from the server. It represents a web page as a document so that client-side scripting languages can change the structure, style, and content. Typically, web pages are transmitted and presented as HTML content, along with instructions on how associated static content should be rendered and displayed. However, this representation can all be manipulated and changed through client-side code talking to the programming interface exposed by the DOM.
The DOM is represented as an object-orientated tree structure of nested elements known as nodes. Each node represents an individual page element, and the DOM allows unique identification of each element in the tree via a dot-delimited and hierarchical “address”. For example a form may have the name “userDetails” and be addressable therefore as “document.userDetails”.
As a brief refresher, both reflected and stored XSS are ultimately “executed” on the client-side but rely on flaws in the handling of input or output processed by the server. That is, there is an input handler on the server somewhere that fails to correctly maintain the separation between executable code and user-provided data under certain conditions (a process that is typically ensured via measures such as robust input validation. The flawed input handler allows an attacker to send malicious data that causes the server to return or “reflect” unsanitized responses back to the client containing the attacker’s input as injected code, which the user’s browser then faithfully executes.
In server-side XSS, input is sent to the web server via HTTP requests using HTTP verbs (methods) such as GET or POST, with the user-supplied input appearing as included (or “reflected”) output unchanged within the response. The malicious input is said to be “reflected” back as part of the output code provided by the server to the client to be executed client-side. However, this same “input-output” mechanism exists client-side, albeit in a very different form, as we will outline below.
As we saw above in relation to the DOM, with every element on the page having an “address” or unique name, client-side code (such as JavaScript) can therefore address a page element programmatically and interact with it. Within a page JavaScript can:
An “event” is what allows webpages to be truly dynamic: it is something that either the browser does, or that the user does (such as “the webpage has finished loading”, or “the user has clicked on an HTML button”) that, via an event listener permits the launching of associated JavaScript code.
However, flaws also exist in client-side handling of input, allowing it to write code into the DOM in an unsafe and unintended manner. Technically, we say that DOM XSS exists if an attacker is able to place data (rather than content) into a source (some input they control and are intended to control) so that it is propagated to a sink (the reflection point that eventually executes, or helps with execution of, the malicious JavaScript injected through the source) that fails to maintain the separation between executable code and client-provided data and therefore permits the execution of arbitrary JavaScript. Sources for the data can include the DOM itself, but also less commonly considered sources such as the result of a returned AJAX “”Asynchronous JavaScript and XML”) call to the server.
The flawed code is ultimately still written by in-house developers, but the difference is that the code is not executed (or any input evaluated) on the server-side; the flawed code is simply handed to the user’s browser as a static resource for it to evaluate and make use of, and the XSS occurs when a specific event fires within the page running in the client’s browser, using user-provided input.
Just as when code is evaluated server-side, the possibility exists for any input passed to client-side code to “break out” of its intended containment as user data and be evaluated as code instead. Specific functions such as eval(), setTimeout() and setInterval() tend to be particularly vulnerable sinks if passed data directly from user input without careful handling or sanitisation within the JavaScript code.
Let’s suppose that the following code is used to create a form to let the user select their preferred language. A default language is also provided in the query string, as the parameter “default”.
Select your language: <select><script> document.write("<OPTION value=1>"+decodeURIComponent(document.location.href.substring(document.location.href.indexOf("default=")+8))+"</OPTION>"); document.write("<OPTION value=2>English</OPTION>"); </script></select>
The page is invoked with a URL such as:
http://www.example.com/page.html?default=French
A DOM Based XSS attack against this page can be accomplished by sending the following URL to a victim:
http://www.example.com/page.html?default=<script>alert(document.cookie)</script>
When the victim clicks on this link, the browser sends a request for:
/page.html?default=<script>alert(document.cookie)</script>
to www.example.com. The server responds with the page containing the above JavaScript code. The browser creates a DOM object for the page, in which the document.location object contains the string:
http://www.example.com/page.html?default=<script>alert(document.cookie)</script>
The original JavaScript code in the page does not expect the default parameter to contain HTML markup, and as such it simply decodes and writes it into the page (DOM) at runtime. The browser then renders the resulting page and executes the attacker’s script:
alert(document.cookie)
Note that the HTTP response sent from the server does not contain the attacker’s payload. This payload manifests itself at the client-side script at runtime, when a flawed script accesses the DOM variable document.location and assumes it is not malicious.
DOM-based XSS is becoming increasingly critical, both in terms of frequency of exploit and impact as more and more web applications implement their UI code using a new wave of fronted web technologies in a paradigm known as Single Page Applications (“SPAs”). These interact with the user by dynamically rewriting the current web page and fetching new data from the web server in the background, instead of the default method of a web browser loading entire new pages. Because SPAs rely more heavily on JavaScript as in integral underpinning of their navigation and operation than traditional web applications (which rely more heavily on server-side processing under a request-response cycle) they are more susceptible to DOM XSS vulnerabilities.
The graph below shows global Google search volumes for the term ‘Single Page Application.’ Note the sharp rise in popularity from around 2011.
Preventing DOM XSS requires sensible precautions be taken during each step of the software development lifecycle (SDLC) to prevent a robust series of measures that together reduce the overall risk:
AppCheck help you with providing assurance in your entire organisation’s security footprint. AppCheck performs comprehensive checks for a massive range of web application vulnerabilities – including DOM XSS vulnerabilities and client-side code weaknesses – from first principle to detect vulnerabilities in in-house application code.
The AppCheck web application vulnerability scanner includes an advanced and custom DOM-based XSS scanning module that can detect DOM XSS vulnerabilities. It does this by having a full native understanding of JavaScript code, including Single Page Applications (SPAs) and renders and evaluates them in the exact same way as a user web browser does.
The AppCheck Vulnerability Analysis Engine provides detailed rationale behind each finding including a custom narrative to explain the detection methodology, verbose technical detail, and proof of concept evidence through safe exploitation.
AppCheck is a software security vendor based in the UK, offering a leading security scanning platform that automates the discovery of security flaws within organisations websites, applications, network, and cloud infrastructure. AppCheck are authorized by the Common Vulnerabilities and Exposures (CVE) Program as a CVE Numbering Authority (CNA).
As always, if you require any more information on this topic or want to see what unexpected vulnerabilities AppCheck can pick up in your website and applications then please get in contact with us: info@localhost
No software to download or install.
Contact us or call us 0113 887 8380
AppCheck is a software security vendor based in the UK, offering a leading security scanning platform that automates the discovery of security flaws within organisations websites, applications, network and cloud infrastructure. AppCheck are authorized by te Common Vulnerabilities and Exposures (CVE) Program aas a CVE Numbering Authority (CNA)