I just noticed some of my Twitter friends posting the following mysterious message:
“Don’t Click: http://tinyurl.com/amgzs6”
It turns out this is a Tweetbomb. If you go to that link and click on the button below, you end up tweeting the same thing:
… thus all your friends see it and some of them click on it and re-post it, and so on, thus propagating the message across the entire Twittersphere.
TinyURL shut down the redirect quickly and Twitter has responded, but the same attack could arise unless measures are taken. Of which, more later.
So how does this hack work?
The hack is an example of clickjacking. (I’ve heard the term a lot, but only understood its meaning after the investigation of this tweetbomb described here.)
Firstly, it’s using an iframe to embed Twitter.com on the page. The iframe is essentially invisible, due to the CSS structure…more on that in a moment. But first, looking at the source of the iframe, we spot our mysterious message:
<iframe src="http://twitter.com/home?status=Don't Click: http://tinyurl.com/amgzs6" scrolling="no"></iframe>
Well, I didn’t know about this before, but it turns out you can set an initial value for the status field by passing in a “status” parameter on the URL. So here, it’s setting the status to out mysterious message. You can try it yourself by visiting http://twitter.com/home?status=Don’t Click: http://tinyurl.com/amgzs6. (It’s safe to do so - nothing will get submitted, honest!) For the lazy and untrusting ;), what you’ll see at that URL is something like this, assuming you’re logged in to Twitter:
However, their job is not done yet, because they also have to trick you into clicking the “update” button. This is where more cunning comes into play. Check out the CSS for the iframe - containing your Twitter.com page - and the fake button contained on this trickster website:
[css] iframe { position: absolute; width: 550px; height: 228px; top: -170px; left: -400px; z-index: 2; opacity: 0; filter: alpha(opacity=0); } button { position: absolute; top: 10px; left: 10px; z-index: 1; width: 120px; } [/css]
And the elements:
<iframe src="http://twitter.com/home?status=Don't Click: http://tinyurl.com/amgzs6" scrolling="no"></iframe> <button>Don't Click</button>
We can see from the CSS z-indexes, the iframe is on top of the button. And we can see from the iframe’s opacity that it is completely invisible. Hmmm…so it’s on top, but completely invisible. If there was a button there, you wouldn’t be able to see it, but you would still be able to click on it. This is where the layout comes in (position, width, height, top, left). The iframe is positioned just right so the the (invisible) update button appears in the top-right of the web page at (10,10), right where the fake button is. So the Update button is directly above the fake button but invisible. The website shows a fake button and tempts you to click on it, but clicking on it causes the real Twitter button - inside the iframe - to fire. Thanks to the URL status hack, you have just submitted their planted message. And of course, you couldn’t see the message, because the iframe is hidden and because that part of the iframe is not present anyway due to the negative left and top settings. There is also some filler text on the trickster page, below the fake button - I guess this is in case some browsers expand the iframe height if nothing else is on the page.
The main way Twitter can prevent this kind of attack is to use the old <a href=”“http://stackoverflow.com/questions/369498/how-to-prevent-iframe-from-redirecting-top-level-window>”bust the iframe” idiom</a>. As I explained in Cross-Domain Communication with IFrames, it’s possible for an iframe to access the URL of its container, and actually re-set that value. Thus the common pattern:
[javascript] if (top.location.href != self.location.href) top.location.href = self.location.href; [/javascript]
If Twitter had that code on its web page, whenever it was loaded inside an iframe, the user would see the entire page clear and refresh to become the Twitter URL that was in the iframe. I actually think it’s a shame they would have to resort to something like that, because there are valid uses for iframes, as WebWait demonstrates. Any site that uses this pattern cannot bathe in the warm glow of WebWait :). But I can see why sites need to take this measure. (Update: Twitter has updated their code to bust out of the cache, just after I posted this!)