As a follow-up to the conference given at Confoo a 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 "e;
). 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.
- Client-side template context
Most special characters are escaped in the HTML context. However, some Javascript libraries loaded on your web page may load dynamic expression from the DOM. It is the case of AngularJS. AngularJS will evaluate expressions between brackets ({{ ... }}
) to its own scripting language. AngularJS expression are powerfull enough to lauch arbitrary JavaScript.
The following page might look safe.
<script src="@Url.Content("~/Scripts/angular-1.6.6.min.js")" type="text/javascript"></script>[...]
<h1>Content:</h1>
@Model.Content
It is possible to execute a malicious JavaScript like this:
{{constructor.constructor('alert(1)')()}}
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.
Request Validation
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="<img src=xx onerror=alert(1)>" >
</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
- OWASP : Cross-site Scripting (XSS)
- XSS (Cross Site Scripting) Prevention Cheat Sheet
- OWASP: XSS Filter Evasion Cheat Sheet
- Bypassing ASP .NET « ValidateRequest » for Stored XSS Attack by InfoSecAuditor
- DOM Clobbering by Gareth Heyes
- Request Validation Whitepaper
- Razor syntax Quick reference
- The misunderstood X-XSS-Protection by File Descriptor
- XSS without HTML: Client-Side Template Injection with AngularJS by Gareth Heyes
CAS D'UTILISATION
Cyberrisques
Mesures de sécurité basées sur les risques
Sociétés de financement par capitaux propres
Prendre des décisions éclairées
Sécurité des données sensibles
Protéger les informations sensibles
Conformité en matière de cybersécurité
Respecter les obligations réglementaires
Cyberassurance
Une stratégie précieuse de gestion des risques
Rançongiciels
Combattre les rançongiciels grâce à une sécurité innovante
Attaques de type « zero-day »
Arrêter les exploits de type « zero-day » grâce à une protection avancée
Consolider, évoluer et prospérer
Prenez de l'avance et gagnez la course avec la Plateforme GoSecure TitanMC.
24/7 MXDR
Détection et réponse sur les terminaux GoSecure TitanMC (EDR)
Antivirus de nouvelle génération GoSecure TitanMC (NGAV)
Surveillance des événements liés aux informations de sécurité GoSecure TitanMC (SIEM)
Détection et réponse des boîtes de messagerie GoSecure TitanMC (IDR)
Intelligence GoSecure TitanMC
Notre SOC
Défense proactive, 24h/24, 7j/7