Stephen Sclafani

A Parsing Quirk and a #NewTwitter XSS

October 4th, 2010

I generally don’t blog about individual XSS issues, however, this particular one was made more interesting as it took advantage of a parsing quirk of browsers that I’ve seen increasingly be an issue as web applications become more “application” like with the help of javascript.

In 2009 the Mikeyy worm was able to inject javascript into user profiles by exploiting the lack of a validity check in the custom colors functionality and the lack of sanitation when displaying the custom colors on the profile. In response Twitter added sanitation and has since implemented a check on the validity of custom colors. An attempt to submit invalidly formatted colors results in an error being returned. In addition to setting custom colors a user can upload a custom background image. When the image is uploaded the user’s current colors are also sent. Twitter failed to extend their validity check to this functionality allowing the colors to be set to any arbitrary value. On both the old and new Twitter custom colors are sanitized when displayed on the profile, however, the profile is not the only place on the new Twitter where a user’s colors are used.

On the loading of the new Twitter a call to the javascript function twttr.API._requestCache.inject is made to display the user’s timeline. Passed to this function is the metadata of the most recent tweets made by the users that the user is following. The metadata of a tweet includes much data that is not displayed, including the profile colors of the user who made it. The colors were included unsanitized.

twttr.API._requestCache.inject("statuses/home_timeline",[{'contributor_details': true, 'include_entities': 1}],[{"retweeted":false,"truncated":false,"geo":null,"entities":{"hashtags":[],"user_mentions":[],"urls":[]},"place":null,"retweet_count":null,"source":"web","favorited":false,"contributors":null,"user":{"contributors_enabled":false,"profile_sidebar_fill_color":"DDEEF6","description":null,"geo_enabled":false,"time_zone":null,"following":true,"notifications":false,"profile_sidebar_border_color":"C0DEED","verified":false,"profile_image_url":"http://s.twimg.com/a/1285026911/images/default_profile_2_normal.png","follow_request_sent":false,"profile_use_background_image":true,"profile_background_color":"\"</script><script>alert('XSS')</script>","screen_name":"stephensclafani","profile_background_image_url":"http://a3.twimg.com/profile_background_images/151950017/h.png","followers_count":0,"profile_text_color":"333333","protected":false,"show_all_inline_media":false,"profile_background_tile":false,"friends_count":0,"url":null,"name":"Stephen Sclafani","listed_count":0,"statuses_count":1,"profile_link_color":"0084B4","id":1598801,"lang":"en","utc_offset":null,"favourites_count":0,"created_at":"Tue Mar 20 07:11:24 +0000 2007","location":null},"id":25168131043,"coordinates":null,"in_reply_to_screen_name":null,"in_reply_to_user_id":null,"in_reply_to_status_id":null,"text":"xss","created_at":"Tue Sep 2120:38:26 +0000 2010"}], 1);

One might expect the injected javascript not to be executed as it’s within a quoted javascript string and that the injected double quote is escaped with a backslash. However, due to a quirk in browser parsing this is not the case. When a browser parses a document and finds a <script> tag it looks for the following </script> tag and ends the block of javascript even if that </script> tag is within a quoted string. By including a </script> tag before the injected block of javascript, the block of javascript being injected into is ended prematurely. This breaks the quoted string allowing the injected block of javascript to be executed.

#NewTwitter XSS

By combining these two issues it was possible for a user to inject javascript into the timelines’ of all of their followers. Twitter was notified of the two issues and has since deployed fixes for both.

For a second example of the quirk see Mike Bailey’s post on a bit.ly XSS.

  • kl

    That’s not a “quirk”, but a well-documented syntax rule. HTML4 forbids </ anywhere in CDATA content, and ECMAScript allows \/ escape for that reason.

  • http://stephensclafani.com/ stephensclafani

    You are correct in that it is a documented rule. However, given the number of Twitter-like instances I’ve seen where it has led to XSS issues I would say that the security implications are not being considered by many developers. Which is why I wrote this post.