Thursday, July 19, 2007

Firefox gets httpOnly!

I don't usually report on things here, but since no-one else seems to be saying something, I thought I should.

Anyway, Firefox has finally implemented httpOnly cookies, in, as you can see from their patch notes, and the following test case:

Note: If httpOnly cookies is implemented, the alert box should be blank, if it is not implemented you should see an alert which says hidden=value


header("Set-Cookie: hidden=value; httpOnly");


So hurrah for the Firefox developers who made this happen, no matter how long it took.

Wednesday, July 11, 2007

Exploiting reflected XSS vulnerabilities, where user input must come through HTTP Request Headers


1.0 Introduction
2.0 The User_Agent Header
3.0 (Known) Firefox & Safari Request Header Injection (Sometimes)
4.0 Attacking Caching Proxies
5.0 References

1.0 Introduction

Ever since Adobe patched Flash player to stop attackers spoofing certain headers[1] such as Referer, User-Agent, etc, it has been considered impossible to exploit XSS vulnerabilities where the user input is taken from a request header, e.g. when a website prints out what User-Agent a user's browser is sending, without escaping it. With the exception of the Referer header which we can control enough to exploit XSS attacks through it.

I want to showcase several ways in which we can still exploit these vulnerabilities.

2.0 The User_Agent header

If you look at how the User-Agent header is accessed in certain languages (namely PHP/Perl/Ruby/ColdFusion), you will see that the User-Agent header is not referenced as it is sent over the wire:





As you can see, all of these languages use an underscore(_) instead of a hyphen(-) when accessing the data, so if we use Flash to send a User_Agent header, like this:

class Attack {
  static function main(mc) {
    var req:LoadVars = new LoadVars();

    req.addRequestHeader("User_Agent", "<script>alert(1)</script>");
    req.send("http://localhost/XSS/server.php", "_self");

(Can be easily compiled with mtasc)

So if any of those languages mentioned above insecurely print the User-Agent header, or any header with a hyphen in it, then no matter whether it is blocked or not, it can still be injected.

The three other languages which I checked where ASP, ASP.NET and Java, and this is how the variables are accessed:



And while ASP/ASP.NET would seem to be vulnerable, it is a known fact to the MS developers[2] that headers with underscores cannot be accessed through the HTTP_ server variables, and so they have added more methods[2], which are not vulnerable.

Java does things neatly, and so it is not vulnerable.

Note: I made a mistake originally, and it seems that Perl apps are only vulnerable when testing through browsers other than IE.

Perl seems to use the last User_Agent or User-Agent header which you send it, and since the Flash plugin in IE appends our User_Agent header before IE's User-Agent header, Perl apps cannot be exploited when the user is Using IE.

3.0 (Known) Firefox & Safari Request Header Injection (Sometimes)

Stefano Di Paola published a paper[3], where he pointed out that IE7 and Firefox both facilitated request splitting when Digest authentication was used, Comcor also pointed out that Safari was vulnerable to the same issue.

IE7 was only vulnerable through the XMLHttpRequest object, which can only be invoked from the website which has Digest authentication, so it is useless to us in this case.

Furthermore request splitting is only useful when a user is behind a proxy (See Note at end of section), so if a user is not behind a proxy, then there is a point to spoofing headers rather than conducting a request splitting attack.

Anyway, what was not mentioned in the paper was that not only can the attack be invoked through an img tag, but it can also be invoked from an iframe tag, so here is a rather contrived PoC (based on Stefano's code):


  header('Set-Cookie: PHPSESSID=6555');

  if((int)intval($_COOKIE['PHPSESSID']) !== 6555){
    header('HTTP/1.0 401 Authorization Required');
    header('WWW-Authenticate: Digest realm="", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"');
  } else {
    header("Set-Cookie: PHPSESSID=0");



  <iframe src="http://user%0aUser-Agent%3a%20%3Cscript%3Ealert%281%29%3C%2Fscript%3E%0aTest%3a%20:pp@localhost/XSS/digest.php"></iframe>

Note: Its not completely true that we can't do any request splitting without a proxy; we can still split requests and have a server with multiple vhosts interpret the second split response as a normal request for a vhost other than that which has the digest authentication. Thanks to Amit Klein for pointing this out.

4.0 Attacking Cache Proxies.

Warning: This attack is rather convoluted, and a bit impractical. *shrug*

With the advent of Anti-DNS Pinning being disclosed by Martin Johns[4] and improved on by Kanatoko Anvil[5][6][7], Kanatoko also found that Flash didn't Pin DNS[8], and who together found that Java's DNS record could be spoofed if used via LiveConnect[9].

This gives us an exceptionally powerful tool - an ability to create socket connections from a victim's computer.

So by utilising the low level socket abilities of Flash or Java we can create a socket connection to the user's caching proxy server if they have one. From there we can inject requests where we provide an XSS payload in the appropriate HTTP request header, which the proxy will sometimes cache, so when we redirect the user to that page they will be served the XSSed version.

This works because cache servers are often setup to cache html pages, and they are very sometimes setup so the only thing which is matches is the URL, rather than any headers or cookies.

Here is a step-by-step explanation:

1. First of all we need to know the IP address and port the users to access their proxy - sadly this part is rather browser specific.

On IE we can use Java[10][11] to detect the proxy settings in IE (I know nothing about Java, other than the fact that its possible, and there are known methods), or by using Javascript[11] in Firefox, by reading the Firefox settings network.proxy.http and network.proxy.http_port.

2. The next step is to open a socket to the proxy server, Anti-DNS Pinning attacks are already well documented in [4][5][6][7][8][9].

3. Send a request to the caching proxy with an XSS payload in the appropriate HTTP headers on the socket you have established.

4. Send the user to the cached page. This can be improved by using Flash to send a "Cache-Control: only-if-cached" header so that the proxy is more likely to serve the XSS-ed page.

Note: I don't have any code to test this, but from when I setup squid to cache html pages, creating a socket to the page and injecting into headers, then viewing the same page in the browser worked, and so I see no reason why this shouldn't. And I have personally seen live proxy servers setup to cache html pages, and where these attacks are possible, so while I'm not sure how common such setups are, they definitely exist.

5.0 References

[1] "Forging HTTP request headers with Flash", Amit Klein, July 2006

[2] "HOWTO: Retrieve Request Headers using ISAPI, ASP, and ASP.Net", David Wang, April 2006

[3] "IE 7 and Firefox Browsers Digest Authentication Request Splitting", Stafano Di Paolo, April 2007

[4] "(somewhat) breaking the same-origin policy by undermining dns-pinning", Martin Johns, August 2006

[5] "Stealing Information Using Anti-DNS Pinning : Online Demonstration", Kanatoko Anvil, December 2006

[6] "Re: DNS Spoofing/Pinning", Kanatoko Anvil, December 2006,4511,4539#msg-4539

[7] "Anti-DNS Pinning + Socket in FLASH", Kanatoko Anvil, February 2007

[8] "Re: DNS Spoofing/Pinning", Kanatoko Anvil, February 2007,4511#msg-6253

[9] "Using Java in anti DNS-pinning attacks (Firefox and Opera)", Martin Johns & Kanatoko Anvil, February 2007

[10] "How to detect Proxy Settings for Internet Connection"

[11] "RE: Auto-detecting proxy settings in a standalone Java app"

[12] "Read Firefox Settings (PoC)", Sergey Vzloman, May 2007