During a recent application penetration test on behalf of a client, one of the security vulnerabilities discovered was a stored cross-site scripting vector, delivered via a JSON request to a MVC3 controller. The malicious data (in this case a simple script tag proof-of-concept) was written to the database and subsequently echoed back to the user when viewing a number of pages within the application.
There are obviously two issues here common with all stored XSS vectors - firstly the lack of adequate input validation as untrusted input was written to the database without any filtering, and secondly, lack of output encoding allowed arbitrary script execution within an unsuspecting victim’s browser.
.NET framework offers a number of “safety nets” in this respect. Within POST and GET requests, if certain characters are entered, a Yellow Screen of Death (YSoD) is often observed with a ‘A potentially dangerous Request.Form value...’ error displayed (a HttpRequestValidationException). This prevents common XSS attacks against an application, but for example, will fail to detect attacks that do not use angle brackets (e.g. HTML attribute based XSS). Secondly, automatic HTML encoding is offered in the Razor view engine, and can be easily used with the Web Forms view (ASP .NET4 and above) by using ‘<%: %>’ syntax. A large number of XSS vulnerabilities we observe in .NET MVC applications return untrusted data within ‘<%= %>’ code nuggets, even if running a framework that supports the new ‘encoded’ code nugget syntax.
Our client’s application in this case generally had good input validation throughout, and in the rare situation where input validation was lacking, a HttpRequestValidationException was thrown by the application. Unfortunately however, no input validation is carried out by the JsonValueProviderFactory class, meaning any unsafe input received in a JSON request would not throw a HttpRequestValidationException. As JSON requests should have the relevant HTTP request headers (‘X-Requested-With: XMLHttpRequest’ and ‘Content-Type: application/json’) set, this effectively rules out reflected XSS via JSON requests. It does not however, prevent stored XSS, as a malicious user has full control over the HTTP request headers sent to an application.
Now this issue is compounded as the JsonValueProviderFactory is a default value provider in MVC3 and above. In other words, the application is likely to process a JSON request as if it were a POST, even if you do not use Json within your application. This issue was documented a couple of years ago here -http://weblogs.asp.net/imranbaloch/archive/2011/05/23/security-issue-in-asp-net-mvc3-jsonvalueproviderfactory.aspx.
To reiterate, HTTPRequestValidation can be easily bypassed to perform stored XSS attacks by simply changing a POST request to a JSON request. The majority of sites written in MVC3 and above are likely vulnerable to this attack.
This is a good example of the danger in over-reliance on the ‘safety nets’ provided by modern frameworks. Developers should ensure strict input validation is performed on all client provided input, ideally using a whitelist approach. Whilst in this case it is possible to implement a custom model binder to implement your own safety nets (refer to previously mentioned article), catching malicious input in this manner should be considered ‘a last resort’.
We have written a Burp Suite extension to allow pentesters to easily convert a POST request to a JSON message, setting the relevant HTTP headers as required to pass server side JSON validation (i.e. checking it has been submitted with the XMLHttpRequest header).
As our client had also implemented Cross Site Request Forgery (CSRF) protection across the application, including for JSON requests using this method, the extension also moves any present ‘__RequestVerificationToken’ to the HTTP request headers. Obviously this is implementation specific, if the token is being passed via some other method (e.g. a GET parameter), you’ll need to manually edit the request before forwarding.
Loading the extension into a recent version of Burp is now easy - simply grab the jar file from our GitHub repository (direct link to download), and load it via the Burp ‘Extender’ tab. For all [editable] POST requests within Burp, you’ll now see a new right-click menu option ‘Change POST Request to JSON’.
Finally, some good best practices for developers using MVC are listed on the MSDN blog here. Pentesters, please ensure you check to see how all MVC applications handle malicious JSON requests on your next test. Enjoy the extension.