The Twitter XSS Post-Game Show
September 7, 2010
Since you’re here, you’ve probably already heard about the Twitter XSS shenanigans from the last few days. The short version: Somebody found a hole in Twitter, and I decided to write a proof of concept. There wasn’t really any motivation, other than the fact that I woke up earlier than usual and felt like coding an exploit. That said, a lot of interesting things came from it, and it spawned a series of related projects that I’ll be working on for months to come. Overall, it was fun.
What Happened
Monday, 4:00 AM: Woke up. Couldn’t sleep, so I logged on to see what was happening in the world. I saw an XSSed.com post for a hole in dev.twitter.com and slapped together the original proof of concept.
The exploit was pretty simple- I created a web page with a button that said “pwn me.” When you clicked it, your browser would be forwarded to the vulnerable page on dev.twitter.com, which would execute arbitrary Javascript, collecting your cookies from the browser and sending them back to my server.
My server would then use your cookies to make requests directly to Twitter, posting a tweet using your session (and by extension, from your account). That tweet, in turn, contained a link to the original exploit page, which all your followers would see. Essentially, this allowed it to spread like any other XSS worm, though as a proof of concept, it did provide ample warning to users about what was going to happen.
4:15: The exploit went live and I tweeted about it. I then went out for breakfast.
Throughout the day, my exploit page was viewed 1823 times. 221 individuals were kind enough to test it and send me unique session cookies. How many of those users didn’t read the warnings and were embarassed by essentially handing me the keys to their Twitter accounts, I can’t say, but only 34 of those tweets are still in the public Twitter feed. Many people tested the exploit, then deleted the resulting tweet. Many more tested the exploit from private accounts. A small group used temporary “test” accounts to play with it.
I wasn’t logging any personal data (I’m not Google, after all), so I only know how many unique, successful exploits took place, which ones are public, and which ones were public, but have disappeared from the timeline.
At some point during the day, a spammer released a “real” version of the same exploit, attacking primarily users in Brazil. Their attack code performed essentially the same cookie theft operation as I did- it is a pretty standard session theft attack, after all. The difference, of course, is that this one was automated, and contained no warning messages for the user. The numbers for this attack are even less reliable than mine, but the malicious bit.ly link currently has over 116 thousand hits. I’m betting the actual compromised sessions are less than half that, given that many users will click the link multiple times, many “clicks” will come from bots, people who aren’t logged into Twitter, etc. The only people who could really say are Twitter, the attacker, and maybe the folks at bit.ly. Regardless, this certainly speaks to the efficiency of automated exploits.
About 4:15 PM: Twitter fixed the original bug, and I took my proof of concept offline.
About 11:30 PM: @kinugawamasato tweeted a second variant of the same exploit. I didn’t see it till the next morning.
Tuesday, about 9:00 AM: I saw the new exploit, adapted my proof of concept, and put it back live. Over the next hour, I collected another dozen sessions, but it didn’t get enough time to pick up steam before things started acting strangely.
12:00 PM: Twitter appeared to have fixed the issue completely, replacing the search form with a standard Google search.
The Technical Details
The original vulnerability was exploited by passing double-urlencoded parameters to the search form on dev.twitter.com. Presumably, those parameters were not normalized properly before the filtering/validation phase, and so malicious Javascript characters could be inserted into the page. This isn’t a remarkably unique vector, or a difficult one to find. I’m surprised it took this long for somebody to look.
When this vulnerability was fixed, the second variant was a bit more arcane, but not really all that different. Parameters sent to the server included UTF-8 encoded characters, which again, were able to squeeze past the filtering/validation phase undetected. Kind of cool, actually.
The server side of how I performed the exploit really isn’t that interesting, but since several people asked to see the code, here it is, in all its bug-ridden, insecure glory. This script does not exist on my servers anymore.
My Analysis
The big question bouncing around the security media echochamber is “what does this mean?” Lots of people have lots of ideas, and most of them are wrong.
First off, Twitter doesn’t have a very good security track record. I doubt this is shocking to you, but it’s yet more evidence that you should be careful about how much trust you place in Twitter itself, and in the tweets of your friends. Something to keep in mind, at least. (As a side note, Twitter has been trying to hire security people for some time, so if you think you’re up to that particular challenge, I’m sure they’d be happy to talk to you). They’ve done impressive things with scalability, data management, and arguably, created a whole new way of communicating with people. Twitter is a very interesting company, but security isn’t something they do well.
The key to these attacks, of course, was faulty input sanitization. Input sanitization is extremely hard to get right, and there is no reason that Twitter should have been manually patching specific vulnerabilities. If they’d used a decent, standardized sanitization library, the issue could have been fixed once and for all, and probably prevented in the first place. I’m a big fan of OWASP’s ESAPI project, and it probably would have helped here.
It is important to note that Twitter’s session cookies are scoped for the entire *.twitter.com domain. This certainly makes it easier for users to stay logged in between, say, the mobile, static, and regular web sites, but it also opens them up to serious issues.
Another XSS hole on any subdomain of Twitter.com (there are many– subdomains, and XSS holes) can be exploited in the exact manner as these last two. If Twitter intends to keep the wide scope on their session cookies, they really should be much more careful about what other applications live on “twitter.com” domains– particularly domains pointed at third-party servers, such as apiwiki.twitter.com and status.twitter.com. In fact, one of these applications has its own XSS holes, and there’s nothing Twitter can do about it (more details will be coming when I’ve coordinated a fix with the vendor, presumably).
Twitter, by the way, is not alone in this problem- virtually every nontrivial site on the web has sessions scoped the same way, and is vulnerable to similar attacks. Web browsers make trust decisions based on domain names (flawed as that system is), and most website owners are far too blasé about the value of their namespace.
XSS is a massive problem. It’s really a pretty basic attack, which is why it’s not all that interesting to most researchers. It’s trivial to find, trivial to exploit, and we quickly move on to other things. The problem, though, has not gone away. As websites grow, scope widens, and browsers gain more functionality, the XSS problem continues to grow steadily worse.
There is absolutely no excuse for a major website like Twitter to not have gone through a serious application security review process, quarantined non-essential applications (such as this developer documentation site) with unique domains and tighter-scoped session controls, and then reviewed the remaining applications for security vulnerabilities. If you build a web property with this much attack surface exposed, trying to find and fix individual bugs is pretty much pointless.
I won’t make any judgment about how smart it was for people to use my exploit, despite the warnings. The fact is, you are vulnerable to these attacks unless you are taking specific measures to prevent them. I’ve seen comments from some people who feel clever about not clicking my (well-labeled) malicious link, but they’re still browsing with Javascript enabled. Trust me: You really can’t trust me. I don’t even browse my own website without both NoScript and RequestPolicy cranked up.
Interestingly, in the times when NoScript is most needed (critical or popular websites that you interact with regularly), you’re the most likely to have it disabled. If you use Twitter’s web interface at all, you have to disable NoScript for that domain– it’s simply not functional without Javascript. The exploit itself didn’t have to use Javascript at all outside the twitter.com domain, so odds are, it could have worked on you even with NoScript installed. Still, NoScript has quite a few other security features that work even when Javascript is enabled, so it’s still useful, even if you globally enable Javascript (though I hope you don’t). I’ve said it before, but for a secure-by-design browser, RequestPolicy is where it’s at.
Final Thoughts
- You really shouldn’t click a link that says “I just got pwned” unless you really know what you’re doing
- XSS attacks are incredibly easy to exploit, and difficult to prevent. This makes them extremely dangerous
- You trust your web browser far too much
- Your web browser trusts me far too much
- NoScript is nice, but when you need it most, you’re probably not using it
- Self-referential hacks are hilarious
–Mike Bailey




Absolutely excellent.
I love this writeup
I’m not enough of a php monkey to follow every iota of the code on your back end, but you’re doing what I suspected you were – forging your own http headers.
To my recollection, the last few twitter vulns I heard about were xss vulns in various places. Without going to look it up, anyway..
Good work!