Tuesday, March 22, 2016

XSS for ASP.net developers

As a follow-up to the conference given at Confoo few weeks ago, we are doing a focus article on the same topic. The presentation was giving an overview of the modern XSS attack vectors and filter bypass. In this blog post, we will take a closer look at XSS in the context of .NET applications.
This article is intended to be a simple checklist for ASP.net MVC developers or security auditors. Defensive measures can be put in place at various layers including the template files (Razor or ASPx Forms), the Request Validation feature and the client-side (browser) filters.


Template


Lets remind ourselves that the root cause of Cross-Site Scripting is missing  encoding of user inputs. Having the right encoding at the source is obviously the true antidote to this class of vulnerabilities. Luckily, the template engine available in .NET is doing some encoding by default.
Here is a list of vulnerable templates using the Razor template engine. The examples assumes that the values displayed are controlled by the user.

Unsafe HTML

Hello @Html.Raw(ViewBag.Name)
If direct binding was used (@ViewBag.Name), it would be encoded properly the value for the HTML context. In the HTML context, special characters are replaced by XML entities such as < , > and ".

Unsafe Html.Raw in attributes

<a href="/ViewDetails?name=@Html.Raw(ViewBag.Name)">View Details</a>
In this unsafe example, the developer might have disabled the encoding because he realizes that the default encoding to HTML entities is not appropriate for URLs. Special characters are replaced by entities (ie " becomes &quote;). A context specific function should have been used. Server.UrlEncode() can be used to encode safely a value in an URL.

Unsafe action link

@Html.ActionLink("Open page", null, null, null, new { href = @ViewBag.Url })
With the first ActionLink, an attacker could use the URL "javascript:[...]" to trigger malicious JavaScript. A validation of the URL is needed in the controller.
@Html.ActionLink("Back to previous page", null, null, null, new { href = Request.UrlReferrer })
The second ActionLink example could lead to an Open Redirect. It does not lead to arbitrary JavaScript execution but it could be used as an effective means for stealing credentials or phishing attacks.

Unsafe "unquoted" attribute

Attributes in Razor template can become vulnerable to XSS in two scenarios: when Html.Raw is used (see Unsafe Html.Raw in attributes) or when an attribute is not placed in quotes. Razor does not enforce the use of single or double quotes.
<img src=logo.png alt=@(ViewBag.ImageId)>
Here a malicious user controlling the ImageId variable could add a JavaScript event such as onload or onerror. The following snippet illustrate a basic injection.
<img src=logo.png alt=''onload='alert(1)'>

Another example with unquote attribute that could lead to DOM clobbering

<form class=@(ViewBag.FormClass) action="SubmitMe">
</form>
In this particular case, a form with the injected properties "name" and "id" can lead to shadow JavaScript global variables that are injected later in the DOM. It is a very capricious attack vector that works for very specific property names. If you want to know more about it, you can read Gareth Heyes article on DOM clobbering.

Script context

<script>
api.SearchUser('@(ViewBag.FirstName)','@(ViewBag.FirstName)');
</script>
Finally, the script context is slightly more at risk because the value are escaped to HTML entities by default. In the example above, it is possible to inject JavaScript because HTML special characters are escaped but not blackslash ().
<script>
api.SearchUser('Dummy\',');prompt(1234)//');
</script>
In the previous example, the blackslash escape the terminating single quote. This cause the string to actually close on the following single quote. The second parameter can then be use by the attacker introduce malicious Javascript.
To summarize the correct guidelines, we have create a cheat sheet that include code examples  for the different contexts. It is intend to be a quick reminder for developers or security auditors doing code review. Feel free to share this cheat sheet to developers of your organisation.

Security Cheat Sheet @ GitHub

Request Validation


RequestValidation error message (input blocked)
Request Validation is a built-in filter to ASP.net. It is an additional layer of protection that is intended to block malicious requests. It will stop suspicious request if the parameters contains any HTML tag.
The Request Validation module is a feature that is integrated to ASP.net framework. It is decoupled from the View ASP forms or Razor Templates. It can be view as a Web Application Firewall for XSS specifically.

Bypasses

Any transformation between the moment the request is received and the construction of the final HTML view can lead to filter bypass. If an HTTP parameter is URL decoded two times, it is enough to bypass the filter.
One of the common transformation is SQL server character conversion to ASCII. It can occurs if the column data type is not unicode (VARCHAR vs NVARCHAR). SQL Server will convert the Unicode character FF1C to 003C. This is sufficient to bypass RequestValidation to create a Stored XSS.

Character Character After storage
U+FF1C (%EF%BC%9C) U+003C (%3C)
U+FF1E (%EF%BC%9E) U+003E (%3E)

A malicious payload using this bypass would look like this:
Name=XSS_HERE_%EF%BC%9Cimg%20src%3Dxxx%20onerror%3Dalert(1)%EF%BC%9E
Once the value would be output, it will be display as < (u+003C) and > (u+003E).
XSS_HERE_<img src=xxx onerror=alert(1)>
Nonetheless, it is still recommended to keep RequestValidation as a extra safe guard. As the official documentation stated, it is not a replacement to validation and proper encoding.

Client-side filters

Modern browsers also provide an additional layer of protection. Chrome, Internet Explorer and Edge have a filter mechanism that can detect some reflected XSS. The browsers will find some patterns were HTML tags are passed in the URL or POST parameters and are reflected in the page.

Browser XSS Filter
Mozilla Firefox None
Google Chrome Yes
Internet Explorer / Edge Yes

The filter can also be forced with the header X-XSS-Protection: 1. This has limited benefits because the filter is already enabled by default in Chrome, Internet Explorer and Edge. However, for some hosts (like localhost), IE/Edge will disable its filter by default.

Additional configurations

  • X-XSS-Protection: 1; mode=block : It is possible to block the loading of the page if a malicious pattern is detected. It is intended to avoid unwanted scripts being disabled.
  • X-XSS-Protection: 1; report=http://myhost.com/report : With this directive the browser will report blocked parameters to the URL specified in the header.
Even though  the filters have been proved to be effective, there is no commitment from the Chrome and Edge teams to provide a complete coverage.

Bypasses


Browser filters do not cover stored XSS and some DOM XSS. They also have some known bypasses. Usually, a transformation will lead to filter bypass. In the following example, the value is properly HTML encoded. However, JavaScript will read the attribute and print it. It will trigger an XSS because once the attribute is read the entities are decoded.
http://website.com/example?userId=%3Cimg%20src=xx%20onerror=alert(1)%3E
<form action="">
<input type="hidden" id="userId" name="userId" value="&lt;img src=xx onerror=alert(1)&gt;" >
</form>

<div id="preview"></div>

<script>
document.getElementById("preview").innerHTML = "Hello "+document.getElementById("userId").value;
</script>


Conclusion


This article should cover the key elements when it comes to safe HTML encoding and XSS prevention. You should now be aware of some of the limitations of each components. It should also stand out that security is not a silver bullet. Relying on a single protection will increase the probability of malicious payload passing through.
As mention previously, feel free to share this cheat sheet to developers of your organisation. The cheat sheet will evolve in the next month to include additional languages and frameworks. The project is open for contribution and reuse.

References


This post was originally posted on GoSecure's blog

No comments:

Post a Comment