Wang Products :: Article
A quick read through the latest vulnerability news on popular security sites such as SecurityFocus or Help Net Security (see our security page for the latest headlines) will usually reveal a number of new exploits found in popular PHP scripts (such as PHP-Nuke, phpBB, Mambo, etc).
Most of the time these exploits will be categorised as "SQL Injection" or "XSS (Cross-Site Scripting)" vulnerabilities, as these are the most common flaws found in PHP/ASP/Perl scripts today. However, in recent months you may have seen reports of scripts being vulnerable to something fairly new - called "HTTP Response Splitting".
The purpose of this FAQ is to give you a brief introduction into what HTTP Response Splitting is, how it occurs, and why it is dangerous.
First of all ? you need to understand what "headers" are in the context of this FAQ article. By "headers", I do not mean HTML page headers or anything to do with html. We are talking about HTTP headers. HTTP headers are small data fields that accompany a Web-based message and help the software that is going to use that message make sense out of it.
For example, request a page using an ordinary web browser and, along with the request itself, a set of HTTP headers will be sent to the server where the page resides. Here are the headers your web browser might send to Google when you visit their site:
GET / HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.7) Gecko/20050414 Firefox/1.0.3
Accept: text/xml,application/xml,application/xhtml+xml,text/html;...
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
This provides the web server with a lot of information it needs to know in order to satisfy your request to view the Google web site ? and it also provides Google with a fair bit of extra information that might be of use (in our example above, it tells them the operating system I use and which web browser I have).
The web server then sends HTTP headers back to your browser containing a lot of information that your browser needs to know (including a status code which tells you the result of your request). Here are the typical headers Google might send back to you:
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html
Content-Encoding: gzip
Server: GWS/2.1
Content-Length: 1040
Date: Tue, 02 May 2005 17:33:58 GMT
If you want to see what HTTP headers your system is sending/receiving ? there are many ways to do so. The best/easiest way is to use a tool like Proxomitron (which acts like a local proxy server for your browser and shows you all HTTP headers that are sent/received), or even better ? if you use the FireFox web browser you can get a plugin called LiveHTTPHeaders to do the job :)
Put simply - HTTP Response Splitting occurs when someone is able to inject their own data into the HTTP headers that the server sends to a web browser. Typically, an attacker will achieve this by crafting a POST or GET request to a vulnerable PHP/ASP/Perl script, containing data designed to add extra HTTP headers which cause the victims browser to do what the attacker wants :)
So, what does the attacker want to achieve by exploiting HTTP Response Splitting? Well, it can be used for the following purposes:
* XSS (Cross-Site Scripting) - HTTP Response Splitting can be used to execute arbitrary HTML/Javascript code in the victims browser (which can lead to cookie/session hijacking) - see our FAQ article entitled "What is Cross Site Scripting (css/xss) ?" for more information on this type of exploit.
* Cache poisoning - the attacker can surf through a proxy server when he exploits the HTTP Response Splitting vulnerability, fooling the proxy server into caching the "injected" HTTP header responses from the server and therefore making the website/server appear defaced to anyone who uses that proxy/cache. HTTP Response Splitting can also be used to poison browser caches, so that the victims browser will cache the defaced/bad page instead of the real one. The cache will continue to give the victim the bad page until their cache is cleared or cleaned.
* Cross user defacement - this is where the attacker makes the web site appear defaced to a particular user (the victim), therefore allowing the attacker to steal session data or cookies etc. It also allows the attacker to steal login information by creating a fake login screen for the website, thus allowing account compromise.
* Hijacking pages - This allows user access to sensitive information, which might be confidential or not normally accessible to the user. With this the attacker can receive the servers response to the client allowing sensitive data from the server to the client to be stolen by the attacker.
HTTP Response Splitting can occur in any script/program that takes user input and outputs it into HTTP headers without filtering the user input for bad characters first (yes, this is yet another vulnerability made possible by poor user input filtering. As we have discussed in previous FAQ articles - you should NEVER trust user input, and should always filter out "bad" characters before you work with variables that contain user input from forms (or query strings via GET).
So - which are the bad characters in this case? Well...the bad characters are actually the carriage return character (CR) and the line-feed character (LF), or as they are more often represented: \r\n:
CR = %0d = \r
LF = %0a = \n
If these characters are not filtered out of user input which gets printed out into HTTP headers by a script/program - then they will be interpreted as a new line, and the start of a new HTTP header item. Can you see where this is going? :)
Lets take a look at what a standard 302 (page redirection) header would look like. This header (when sent to your web browser) causes your browser to redirect from whichever page you requested, to some other page which is specified in the header. The PHP code to generate this 302 redirection header, is as follows:
header ("Location: " . $_GET['page']);
?>
This code above outputs a "Location" header to your browser, which causes your browser to redirect to the page specified in the GET "page" string. So for example, if you accessed the above PHP script like this:
http://www.example.com/thescript.php?page=http://www.wangproducts.com
You would be redirected to http://www.wangproducts.com because your web browser would receive the following headers:
HTTP/1.1 302 Found\r\n
Date: Thu, 06 May 2005 12:15:10 GMT\r\n
Server: Apache/1.3.33 (Unix) mod_ssl/2.8.22 OpenSSL/0.9.7d\r\n
Location: http://www.wangproducts.com\r\n
Transfer-Encoding: chunked\r\n
Content-Type: text/html\r\n
Now www.wangproducts.com will respond with a normal 200 Found response and the user will see the pages at www.wangproducts.com. Notice the "\r\n" carriage return and line feed characters at the end of each line in the HTTP headers.
What you should be thinking at this point is...what happens if I inject my own \r\n characters into the GET "page" string, and then add my own headers after? :) Will they be interpreted as extra headers and sent to my browser? The answer is - yes :) as long as the PHP script does not filter the \r\n out (which our PHP script above does not).
Let's test this out! What if we were to alter the URL above to contain a \r\n (we will add them as %0d and %0a, because this is the method of representing \r and \n respectively when they are provided via a GET/query string). How about - we add our own 200 Found header into the server headers, and then add some HTML after, so that the victims browser will display the HTML instead of redirecting to www.wangproducts.com :) Here is how we do this:
http://www.example.com/thescript.php?page=%0d%0aContent-Length:
%200%0d%0aContent-Type:%20text/html%0d%0aHTTP/1.1%20200%20OK%0d%
0aContent-Type:%20text/html%0d%0a%0d%0a%3Chtml%3E%3Cb%3EI%20HACK
ED%20YOU%3C/b%3EI%3C/html%3E
(put the above URL all on one line).
I know this looks insane but...when you decode the URL-encoding, this translates to us inputting the following into the GET "page" variable:
\r\n
Content-Length: 0\r\n
Content-Type: text/html\r\n
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
I HACKED YOU
Since the PHP code will not filter out our \r\n and passes our data directly into the header() function - our headers will be injected into the HTTP headers and sent to our browser. This means we have just temporarily "defaced" the site :) The HTTP headers your browser will actually receive are as follows:
HTTP/1.1 302 Found
Date: Thu, 06 May 2005 12:15:10 GMT
Server: Apache/1.3.33 (Unix) mod_ssl/2.8.22 OpenSSL/0.9.7d
Location:
Content-Length: 0
Content-Type: text/html
HTTP/1.1 200 OK
Content-Type: text/html
I HACKED YOU
Transfer-Encoding: chunked
Content-Type: text/html
As we can see in above, the server runs the normal 302 response, the arbitrary input we gave in the location header causes it to start a new 200 OK response which shows our inputted data to the user as a normal web server response. This technique is a type of XSS (cross-site scripting) and if you use the right javascript code, you could make it so that the HTTP Response Splitting vulnerability executes code in the victims browser which leads to their cookie/session being hijacked.
If you want to test the cache poisoning implications of this vulnerability, all you need to do is inject Last-Modified, Cache-Control, or Pragma headers into the HTTP headers - so that the "defaced" page becomes cached. You can lookup how to use headers like Last-Modified in a good search engine like Google :)
So - how do we defend against this if we are a PHP/ASP/Perl coder? Simple - by correctly filtering user input! You should NEVER trust user input unless it has been filtered (and even then...don't trust it! always be cautious). For examples of how to filter user input in PHP/ASP - see my other article "SQL Injection Vulnerabilities".
Some of you might be thinking - is this even worth a whole article? is this a real vulnerability? I mean...who on earth writes PHP code that passes user input into header() functions!! right? ... well the sad answer is, many people do...and many large/popular sites/scripts are vulnerable to this type of attack. Want to see a real life example? check out the recent Phorum vulnerability (Phorum is a well-known PHP forum script, like phpBB).
Sources for this article:
* http://www.phpfreaks.com/tutorials/132/0.php
* http://www.packetstormsecurity.org/papers/general/whitepaper_httpresponse.pdf
* http://www.securityfocus.com/archive/1/393953