Circumventing a Blacklist to Exploit Cross-Site Scripting

Cross-site scripting (XSS) continues to remain a prevalent vulnerability in web applications, having ranked in the OWASP Top Ten for 2017. XSS is a type of injection attack where malicious scripts are injected into a trusted website, abusing the user’s trust in said website. The Financial Services website I was examining for this attack vector evaluated risk management.

The XSS attack comes in three different forms: Reflected, stored, and DOM. Of the three, stored XSS is the most severe. The naïve approach in mitigating cross-site scripting would be to use a blacklist; however, circumventing a blacklist can become very trivial due to the flexibility of HTML and JavaScript and how the browser parses them. In this example, I will go through an example of a stored XSS vulnerability found during an assessment on an application that implemented a blacklist as a defense, and how I managed to get around it. The target risk management application employed many functionalities which tested vulnerable to either reflective or stored XSS. In this particular case, I focused on the functionality that let an administrator create document templates.

Creating a document template was a two-step process: the first step designated a name for the template, and the second was selecting the parameters for the template. After creating a test template, I noted on the page listing the templates that the template names were reflected in the source code as such:

Targeting the reflection in the anchor tag, the idea was to get an event handler attribute in the tag such that user interaction with the link triggered the injected payload. The initial attempt of using special characters in the template name was met with an error message stating only alphanumeric characters can be used; further digging into the source code revealed it was just a client-side check that can be bypassed using a proxy tool such as Burp Suite.

Using the test string “xss=”xss, I came across another issue. The inputted template name was being reflected on the second step of the creation process and being interpreted as code, so saving the new template would not set the same template name that was initially inputted. Using HTML entity encoding, I was able to resolve the issue with the test string &quot;xss=&quot;xss.

Now that I was able to inject any attribute into the anchor tag, it was time to test injecting an event handler attribute with some JavaScript. Opting to use the onmouseover event handler, I crafted this initial payload of:

&quot;onmouseover=&quot;alert(document.cookie)&quot;

This is where I was met with the blacklist. After playing around with various functions and parameters, I determined that blacklist was stripping any mention of various JavaScript functions (e.g. alert, prompt, confirm) from the template name, along with property accessors of some objects (e.g. window.xxx, document.xxx). Circumventing the JavaScript function blacklist required usage of JavaScript encoding, so alert became u0061lert. For the property accessor, I used the JavaScript bracket notation so document.cookie became document[‘cookie’].

Combining these evasion techniques, the final payload to illustrate the success of an XSS attack was:

With the newly-created template containing the injected payload, simply moving the cursor over the template name on the template list page triggered execution of the script.

As demonstrated, avoiding the blacklist can require the use of various filter evasion techniques that may be trivial. The naïve blacklist that blocks words or patterns of words can be circumvented by using context-specific encodings that the browser will then decode and interpret as code e.g. using JavaScript encoding when the landing space is a JavaScript context. Alternative forms of JavaScript function calls or property accessors are also useful in getting around the blacklist. Because of the flexibility of HTML and JavaScript, bypassing a blacklist filter can be done with small but informed effort.

Preventing XSS requires treating untrusted data separate from the application code. Input validation can be used for untrusted data to ensure that the data is properly formed for the context it is received, both syntactically and semantically. However, output encoding should be the primary method of preventing XSS. Untrusted data, such as that from user input, should be contextually encoded when displayed so that it is treated as data and not as code. Additional mitigations against the impact of XSS include setting the HTTPOnly flag on the session cookie, implementing Content Security Policy, and using the X-XSS-Protection response header. Following these strategies will work much better than trying to use a blacklist to prevent XSS.

Cookie Use

We use cookies to store information on your computer that are either essential to make our site work or help us personalize and improve the user experience. By using this site, you consent to the placement of these cookies. To learn more, see our Cookie Policy.