DRAFT

this post isn't yet complete

This is mostly in regards to Facebook, but will probably work on other sites - twitter, google+, etc - equally well.

I'm working on http://bookmarkleteer.com, which is pretty simple concept - take some script, inject it on a page, have a good time.

Although most useful for things like Katamari Damacy and Destroy the Web

Something like Destroy the Web is best played on facebook (or any site with a boat ton of seemlingly never-ending content), but sadly, Facebook blocks every imaginable form of cross-domain communication!

It's ironic, seeing as how they exploit every form of cross-domain communication for their widgets on other sites. But they've been doing it for years so they're the perfect example of being very aware of what security holes exist and how to exploit them and, in this case, how to defend against them!

Protection

HTTPS

By defaulting to HTTPS, you automatically prevent scripts from loading on non-http sites.

That's not too big of a deal though, you might be able to find a way to host your scripts on some https site or just take literally 15 minutes to get your own certificate through Comodo or a little longer through StartSSL

Bam now you don't have that restriction.

Headers: strict-transport-security

strict-transport-security: max-age=60

Require that the connection be upgraded to HTTPS or the browser should refuse to serve the content.

read more...

Headers: content-security-policy

content-security-policy:default-src *;script-src https://*.facebook.com http://*.facebook.com https://*.fbcdn.net http://*.fbcdn.net *.facebook.net *.google-analytics.com *.virtualearth.net *.google.com 127.0.0.1:* *.spotilocal.com:* chrome-extension://lifbcibllhkdhoafpjfnlhfpfgnpldfl 'unsafe-inline' 'unsafe-eval' https://*.akamaihd.net http://*.akamaihd.net;style-src * 'unsafe-inline';connect-src https://*.facebook.com http://*.facebook.com https://*.fbcdn.net http://*.fbcdn.net *.facebook.net *.spotilocal.com:* https://*.akamaihd.net ws://*.facebook.com:* http://*.akamaihd.net https://fb.scanandcleanlocal.com:*;

This basically enumerates every possible resource from which a script may be loaded. Fortunately bookmarklets aren't blocked by this.

The only other potential exploit that I can see here (not entirely understanding the gibberish) is if you happen to have access to a localhost server on the user's machine that you can use for communication.

Read more on developer.mozilla.org and github.com/blog

Headers: x-content-type-options

x-content-type-options:nosniff

This prevents internet explorer from interpretting text/plain as html or image/jpef as gif when the server is sending the incorrect content type.

read more...

Headers: x-frame-options

x-frame-options:DENY

One thing I thought of was to load the bookmarklet, load an iFrame, redirect the iFrame to facebook with escaped code in the href (i.e. www.facebook.com?something#encode-uri-escaped-code-here), but I couldn't do that either.

Headers: X-XSS-Protection

x-xss-protection:0

window.postMessage

// prevent any 3rd party sites from sending data back through a popup or iframe
window.addEventListener('message', function (ev) {
  ev.stopPropagation();
  ev.preventDefault();
  return;
});

Getting around it

But here's how I got around it:

Getting the script on the page

Bookmarklet. I don't think there's any other way aside from the console - and you're certainly not getting many users to do that!

Sending Data:

iframe = document.createElement('iframe');
iframe.src = "//bookmarkleteer.com"
iframe.addEventListener('load', function () {
  iframe.contentWindow.postMessage({}, iframe.src)
})
document.appendChild(iframe);

This works like a champ. Unfortunately, it's blocked in the other direction

Possibility:

Use the Like URL, which isn't iframe-deny protected:

Here's a typical Like widget (the kind you see on blogs and news sites):

http://www.facebook.com/plugins/like.php?api_key=213439508797174&locale=en_US&sdk=joey&channel_url=http%3A%2F%2Fstatic.ak.facebook.com%2Fconnect%2Fxd_arbiter.php%3Fversion%3D25%23cb%3Df7fc668b8%26origin%3Dhttp%253A%252F%252Fblog.coolaj86.com%252Ff22898a62c%26domain%3Dblog.coolaj86.com%26relation%3Dparent.parent&href=http%3A%2F%2Fblog.coolaj86.com&node_type=link&width=450&layout=button_count&colorscheme=light&show_faces=false&send=true&extended_social_context=false

It is not iframe-deny protected, so it's possible for me to communicate with that page from the page I'm at

iframe = document.createElement('iframe')
// then redirect to the Like url,
// but replace some of the arguments with arbitrary encodeURIComponent encoded script
iframe.src = "https://bookmarkleteer.com/whatever"
document.body.appendChild(iframe)
iframe.addEventListener('load', function () {
  scriptText = decodeThing(iframe.contentWindow.location.href)
  var g = document.createElement('script');
  var s = document.getElementsByTagName('script')[0];
  g.text = scriptText;
  s.parentNode.insertBefore(g, s);
});

The downside is that you have to find such a security hole on a case-by-case basis.

If you were actually trying to do something site specific, that's not as big of a problem though, because you'd be targetting a particular site anyway, but something generic like Destroy the Web isn't a great candidate for this approach.

Another way to Receive Data (works on all sites?):

As sucky and tedius as this may be, it's still a win:

document.createElement('img')
img = 'https://bookmarkleteer.com/favicon.ico';
img.addEventListener('load', function () {
  console.log(img.width);
  console.log(img.height);
})

If you used phrase-based programming approach you could perhaps communicate that 1 means $.get('/whatever') and 2 means $.click('.thingy') and so forth.

The width could be the function and the height the arguments.


By AJ ONeal

If you loved this and want more like it, sign up!


Did I make your day?
Buy me a coffeeBuy me a coffee  

(you can learn about the bigger picture I'm working towards on my patreon page )