Thursday 23 July 2015

XSS, Extensions and Content-Types

In this post I'll look at which Content-Types and Extensions can actually be used for XSS in modern browsers.

Why does Content-Type and Extension matter?

Applications come in many different shapes and sizes and XSS can occur all over the place. It's easy to assume that just because an application returns you a response with unfiltered/unencoded output you've found an exploitable XSS issue but this is often not the case. Browsers render content based on a number of factors including content-type returned by the server, page content and page extension, without the correct combination XSS won't be possible.

To try and get a definitive answer of what works and what doesn't I thought I'd do some testing. I used multiple file types each containing their legitimate content however I also embedded a JavaScript alert payload in each. Each test case was ran against Chrome 43, IE 11 and Firefox 39. In the tables below "yes" means the payload rendered and "no" means it didn't.

Test #1 - Following Best Practice

In the first test I forced the server to return the correct extension and content-type for each test page.

Extension Chrome IE Firefox
None None yes yes yes
text/plain txt no no no
text/html html yes yes yes
application/javascript js no no no
application/json json no no no
application/xml xml yes yes yes
text/css css no no no
image/jpeg jpeg no no no

Following best practice modern browsers appeared to be pretty secure. The one exception appears to be XML, it seems odd that the browser would allow the rendering of JavaScript in an XML file.

Test #2 - Modifying Extension

In the second test I made the server return the correct content-type but forced a .html extension.

Extension Chrome IE Firefox
None html yes yes yes
text/plain html no no no
text/html html yes yes yes
application/javascript html no yes no
application/json html no no no
application/xml html yes yes yes
text/css html no no no
image/jpeg html no no no

This test seemed to show that browsers will prioritize the use of the content-type over the extension.

Test #3 - Using a text/html Content-Type

In the third test I used the correct extension for each file but made the server return a text/html content-type.

Extension Chrome IE Firefox
text/html None yes yes yes
text/html txt yes yes yes
text/html html yes yes yes
text/html js yes yes yes
text/html json yes yes yes
text/html xml yes yes yes
text/html css yes yes yes
text/html jpeg yes no yes

It looks like browsers rely heavily on the content-type returned by the server. It doesn't matter about the extension or contents of the file, the browser will blindly follow the content-type returned.

Test #4 - Using a text/plain Content-Type

Next I used the correct extension for each file but made the server return a text/plain content-type.

Extension Chrome IE Firefox
text/plain txt no no no
text/plain html no no no
text/plain js no no no
text/plain json no no no
text/plain xml no no no
text/plain css no no no
text/plain jpeg no no no

When using a content-type of text/plain none of the payloads executed. I can't remember for sure, but I believe in the past text/plain used to allow XSS? Looks like this is no longer possible.

Test #5 - MIME sniffing

In this last test case I accessed a page that had no extension and received no content-type header from the server. This should have caused the browser to fall back on mime sniffing to render the page.

Content-Type Extension Chrome IE Firefox
text/plain None None no yes no
text/html None None yes yes yes
application/javascriptNoneNone yes yes yes
application/json None None no yes no
application/xml None None no yes no
text/css None None no yes no
image/jpeg None None no no no

Both Chrome and Firefox refused to render pages that contained additional non-html content. IE however didn't seem to care what the page content was, if it contained HTML it rendered!


Performing application testing on a regular basis I find content-type and extension XSS issues arise quite a lot. This test showed that modern browsers are relatively secure as long as the correct content-type is returned. All browsers also seemed to implement the same analysis techniques aside from IE which seemed slightly more permissive.

Hopefully you guys have found this post useful, if anyone has any suggestions for bypasses to achieve XSS in any of the above, or if I've listed any findings incorrectly (quite possible) drop me a message below :)

Pwndizzle out.