SQL injection

PortSwigger Academy – SQL injection

Ho seguito il percorso di apprendimento suggerito da “PortSwigger Academy” per essere indirizzato nella giusta direzione. Consiglio di iscriversi alla piattaforma, seguire le lezioni e soprattutto svolgere e completare i lab 🙂

SQL injection

Cos’è l’ SQL injection (SQLi)?

SQL injection (SQLi) è una vulnerabilità della sicurezza Web che consente a un utente malintenzionato di interferire con le query che un’applicazione effettua al proprio database. Generalmente consente a un utente malintenzionato di visualizzare dati che normalmente non è in grado di recuperare. Ciò potrebbe includere dati appartenenti ad altri utenti o qualsiasi altro dato a cui l’applicazione stessa è in grado di accedere. In molti casi, un utente malintenzionato può modificare o eliminare questi dati, causando modifiche persistenti al contenuto o al comportamento dell’applicazione.

In alcune situazioni, un utente malintenzionato può intensificare un attacco SQL injection per compromettere il server sottostante o un’altra infrastruttura back-end oppure eseguire un attacco denial-of-service.

Qual è l’impatto di un attacco SQL injection riuscito?

Un attacco SQL injection riuscito può comportare l’accesso non autorizzato a dati sensibili, come password, dettagli della carta di credito o informazioni personali dell’utente. Molte violazioni dei dati di alto profilo negli ultimi anni sono state il risultato di attacchi SQL injection, che hanno portato a danni alla reputazione e multe normative. In alcuni casi, un utente malintenzionato può ottenere una backdoor persistente nei sistemi di un’organizzazione, portando a una compromissione a lungo termine che può passare inosservata per un periodo prolungato.

Come rilevare le vulnerabilità di SQL injection

La maggior parte delle vulnerabilità di SQL injection può essere trovata in modo rapido e affidabile utilizzando lo scanner di vulnerabilità Web di Burp Suite.

L’SQL injection può essere rilevata manualmente utilizzando una serie sistematica di test su ogni punto di ingresso nell’applicazione. Ciò comporta in genere:

  • Inserendo il carattere di apice singolo e cercando errori o altre anomalie.
  • Invio di una sintassi specifica di SQL che restituisce il valore di base (originale) del punto di ingresso e un valore diverso e ricerca di differenze sistematiche nelle risposte dell’applicazione risultanti.
  • Invio di condizioni booleane come OR 1=1 e OR 1=2 e ricerca delle differenze nelle risposte dell’applicazione.
  • Invio di payload progettati per attivare ritardi temporali durante l’esecuzione all’interno di una query SQL e ricerca delle differenze nel tempo impiegato per rispondere.
  • Invio di payload OAST progettati per attivare un’interazione di rete fuori banda quando eseguita all’interno di una query SQL e monitoraggio di eventuali interazioni risultanti.

SQL injection in diverse parti della query

La maggior parte delle vulnerabilità di SQL injection si verifica all’interno della clausola WHERE di una query SELECT. Questo tipo di SQL injection è generalmente ben compreso dai tester esperti.

Ma le vulnerabilità di SQL injection possono in linea di principio verificarsi in qualsiasi posizione all’interno della query e all’interno di diversi tipi di query. Le altre posizioni più comuni in cui si verifica l’iniezione SQL sono:

  • Nelle istruzioni UPDATE, all’interno dei valori aggiornati o nella clausola WHERE.
  • Nelle istruzioni INSERT, all’interno dei valori inseriti.
  • Nelle istruzioni SELECT, all’interno del nome della tabella o della colonna.
  • Nelle istruzioni SELECT, all’interno della clausola ORDER BY.

Esempi di iniezione SQL

Esiste un’ampia varietà di vulnerabilità, attacchi e tecniche di SQL injection, che si verificano in situazioni diverse. Alcuni esempi comuni di SQL injection includono:

Recupero di dati nascosti

Considera un’applicazione per lo shopping che mostra i prodotti in diverse categorie. Quando l’utente fa clic sulla categoria Regali, il browser richiede l’URL:

https://insecure-website.com/products?category=Gifts

Ciò fa sì che l’applicazione esegua una query SQL per recuperare i dettagli dei prodotti rilevanti dal database:

SELECT * FROM products WHERE category = ’Gifts’ AND released = 1

Questa query SQL chiede al database di restituire:

  • tutti i dettagli (*)
  • dalla tabella dei prodotti
  • dove la categoria è Regali
  • e rilasciato è 1.

La restrizione released = 1 viene utilizzata per nascondere i prodotti che non sono stati rilasciati. Per i prodotti non rilasciati, presumibilmente released = 0.

L’applicazione non implementa alcuna difesa contro gli attacchi SQL injection, quindi un utente malintenzionato può costruire un attacco come:

https://insecure-website.com/products?category=Gifts’–

Ciò si traduce nella query SQL:

      SELECT * FROM products WHERE category = ’Gifts’--’ AND released = 1

La cosa fondamentale qui è che la sequenza di doppio trattino è un indicatore di commento in SQL e significa che il resto della query viene interpretato come un commento. Ciò rimuove efficacemente il resto della query, quindi non include più AND released = 1. Ciò significa che vengono visualizzati tutti i prodotti, inclusi i prodotti non rilasciati.

Andando oltre, un utente malintenzionato può fare in modo che l’applicazione visualizzi tutti i prodotti in qualsiasi categoria, comprese le categorie di cui non è a conoscenza:

https://insecure-website.com/products?category=Gifts’+OR+1=1–

La query modificata restituirà tutti gli elementi in cui la categoria è Regali o 1 è uguale a 1. Poiché 1=1 è sempre vero, la query restituirà tutti gli elementi.

Avvertimento

Prestare attenzione quando si inserisce la condizione OR 1=1 in una query SQL. Anche se questo può essere innocuo nel contesto iniziale in cui stai effettuando l’iniezione, è normale che le applicazioni utilizzino i dati di una singola richiesta in più query diverse. Se la tua condizione raggiunge un’istruzione UPDATE o DELETE, ad esempio, ciò può comportare una perdita accidentale di dati.

Lab: SQL injection vulnerability in WHERE clause allowing retrieval of hidden data

(torna su)

Sovvertire la logica dell’applicazione

Considera un’applicazione che consente agli utenti di accedere con un nome utente e una password. Se un utente invia il nome utente wiener e la password bluecheese, l’applicazione controlla le credenziali eseguendo la seguente query SQL:

SELECT * FROM users WHERE username = ’wiener’ AND password = ’bluecheese’

Se la query restituisce i dettagli di un utente, l’accesso ha esito positivo. In caso contrario, viene rifiutato.

In questo caso, un utente malintenzionato può accedere come qualsiasi utente senza password semplicemente utilizzando la sequenza di commenti SQL, per rimuovere il controllo della password dalla clausola WHERE della query. Ad esempio, inviando il nome utente administrator’– e una password vuota si ottiene la seguente query:

SELECT * FROM users WHERE username = ’administrator’--’ AND password =’’

Questa query restituisce l’utente il cui nome utente è administrator e fa accedere correttamente l’attaccante come quell’utente.

Lab: SQL injection vulnerability allowing login bypass

(torna su)

Recupero di dati da altre tabelle di database

Nei casi in cui i risultati di una query SQL vengono restituiti all’interno delle risposte dell’applicazione, un utente malintenzionato può sfruttare una vulnerabilità di SQL injection per recuperare i dati da altre tabelle all’interno del database. Questa operazione viene eseguita utilizzando la parola chiave UNION, che consente di eseguire un’ulteriore query SELECT e aggiungere i risultati alla query originale.

Ad esempio, se un’applicazione esegue la seguente query contenente l’input dell’utente “Gift”:

SELECT name, description FROM products WHERE category = ’Gifts’

quindi un utente malintenzionato può inviare l’input:

’ UNION SELECT username, password users--

Ciò farà sì che l’applicazione restituisca tutti i nomi utente e le password insieme ai nomi e alle descrizioni dei prodotti.

Per saperne di più

SQL injection UNION attacks

Vulnerabilità di blind SQL injection

Molte istanze di SQL injection sono vulnerabilità cieche. Ciò significa che l’applicazione non restituisce i risultati della query SQL o i dettagli di eventuali errori del database all’interno delle sue risposte. Le vulnerabilità cieche possono ancora essere sfruttate per accedere a dati non autorizzati, ma le tecniche coinvolte sono generalmente più complicate e difficili da eseguire.

A seconda della natura della vulnerabilità e del database interessato, è possibile utilizzare le seguenti tecniche per sfruttare le vulnerabilità della blind SQL injection:

  • È possibile modificare la logica della query per attivare una differenza rilevabile nella risposta dell’applicazione a seconda della verità di una singola condizione. Ciò potrebbe comportare l’inserimento di una nuova condizione in una logica booleana o l’attivazione condizionale di un errore come una divisione per zero.
  • È possibile attivare in modo condizionale un ritardo nell’elaborazione della query, consentendo di dedurre la verità della condizione in base al tempo impiegato dall’applicazione per rispondere.
  • È possibile attivare un’interazione di rete fuori banda utilizzando le tecniche OAST. Questa tecnica è estremamente potente e funziona in situazioni in cui le altre tecniche non funzionano. Spesso puoi estrarre direttamente i dati tramite il canale fuori banda, ad esempio inserendo i dati in una ricerca DNS per un dominio che controlli.

Per saperne di più

Blind SQL injection

Iniezione SQL di secondo ordine

L’iniezione SQL di primo ordine si verifica quando l’applicazione prende l’input dell’utente da una richiesta HTTP e, nel corso dell’elaborazione di tale richiesta, incorpora l’input in una query SQL in modo non sicuro.

Nell’iniezione SQL di secondo ordine (nota anche come iniezione SQL archiviata), l’applicazione riceve l’input dell’utente da una richiesta HTTP e lo archivia per un utilizzo futuro. Questo di solito viene fatto inserendo l’input in un database, ma non si verifica alcuna vulnerabilità nel punto in cui i dati sono archiviati. Successivamente, durante la gestione di una richiesta HTTP diversa, l’applicazione recupera i dati archiviati e li incorpora in una query SQL in modo non sicuro.

L’iniezione SQL di secondo ordine si verifica spesso in situazioni in cui gli sviluppatori sono a conoscenza delle vulnerabilità dell’iniezione SQL e quindi gestiscono in modo sicuro il posizionamento iniziale dell’input nel database. Quando i dati vengono successivamente elaborati, sono considerati sicuri, poiché sono stati precedentemente inseriti nel database in modo sicuro. A questo punto, i dati vengono gestiti in modo non sicuro, perché lo sviluppatore li ritiene erroneamente attendibili.

Esame del database

Alcune funzionalità principali del linguaggio SQL sono implementate allo stesso modo su piattaforme di database popolari e così tanti modi per rilevare e sfruttare le vulnerabilità di SQL injection funzionano in modo identico su diversi tipi di database.

Tuttavia, ci sono anche molte differenze tra i database comuni. Ciò significa che alcune tecniche per rilevare e sfruttare l’iniezione SQL funzionano in modo diverso su piattaforme diverse. Per esempio:

  • Sintassi per la concatenazione di stringhe.
  • Commenti.
  • Query in batch (o in pila).
  • API specifiche della piattaforma.
  • Messaggio di errore.

Per saperne di più

SQL injection cheat sheet

Dopo l’identificazione iniziale di una vulnerabilità SQL injection, è generalmente utile ottenere alcune informazioni sul database stesso. Queste informazioni possono spesso aprire la strada a un ulteriore sfruttamento.

È possibile interrogare i dettagli della versione per il database. Il modo in cui ciò viene fatto dipende dal tipo di database, quindi puoi dedurre il tipo di database da qualsiasi tecnica funzioni. Ad esempio, su Oracle puoi eseguire:

SELECT * FROM v$version

È inoltre possibile determinare quali tabelle di database esistono e quali colonne contengono. Ad esempio, sulla maggior parte dei database è possibile eseguire la seguente query per elencare le tabelle:

SELECT * FROM information_schema.tables

Per saperne di più

SQL injection in diversi contesti

In tutti i laboratori finora, hai utilizzato la stringa di query per iniettare il tuo payload SQL dannoso. Tuttavia, è importante notare che è possibile eseguire attacchi SQL injection utilizzando qualsiasi input controllabile elaborato come query SQL dall’applicazione. Ad esempio, alcuni siti Web accettano input in formato JSON o XML e lo utilizzano per interrogare il database.

Questi diversi formati possono persino fornire modi alternativi per offuscare gli attacchi (obfuscate attacks) che altrimenti sarebbero bloccati a causa di WAF e altri meccanismi di difesa. Le implementazioni deboli spesso cercano solo parole chiave SQL injection comuni all’interno della richiesta, quindi potresti essere in grado di aggirare questi filtri semplicemente codificando o eseguendo l’escape dei caratteri nelle parole chiave proibite. Ad esempio, la seguente SQL injection basata su XML utilizza una sequenza di escape XML per codificare il carattere S in SELECT:

<stockCheck>
	<productId>
	123
	</productId>
	<storeId>
	999 &#x53;ELECT * FROM information_schema.tables
	</storeId>
	</stockCheck>

Questo verrà decodificato sul lato server prima di essere passato all’interprete SQL.

Lab: SQL injection with filter bypass via XML encoding

Come prevenire l’SQL injection

La maggior parte delle istanze di SQL injection può essere prevenuta utilizzando query con parametri (note anche come istruzioni preparate) anziché la concatenazione di stringhe all’interno della query.

Il seguente codice è vulnerabile all’iniezione SQL perché l’input dell’utente è concatenato direttamente nella query:

String query = "SELECT * FROM products WHERE category = '"+ input + "'";
    Statement statement = connection.createStatement();
    ResultSet resultSet = statement.executeQuery(query);

Questo codice può essere facilmente riscritto in modo da impedire all’input dell’utente di interferire con la struttura della query:

PreparedStatement statement = connection.prepareStatement("SELECT * FROM products WHERE category = ?");
    statement.setString(1, input);
    ResultSet resultSet = statement.executeQuery();

Le query con parametri possono essere utilizzate per qualsiasi situazione in cui l’input non attendibile viene visualizzato come dati all’interno della query, inclusi la clausola WHERE e i valori in un’istruzione INSERT o UPDATE. Non possono essere utilizzati per gestire l’input non attendibile in altre parti della query, come i nomi di tabelle o colonne o la clausola ORDER BY. La funzionalità dell’applicazione che inserisce dati non attendibili in quelle parti della query dovrà adottare un approccio diverso, ad esempio inserire nella whitelist i valori di input consentiti o utilizzare una logica diversa per fornire il comportamento richiesto.

Affinché una query con parametri sia efficace nel prevenire l’iniezione SQL, la stringa utilizzata nella query deve essere sempre una costante hardcoded e non deve mai contenere dati variabili di alcuna origine. Non essere tentato di decidere caso per caso se un elemento di dati è attendibile e continua a utilizzare la concatenazione di stringhe all’interno della query per i casi considerati sicuri. È fin troppo facile commettere errori sulla possibile origine dei dati o che le modifiche in altro codice violino le ipotesi su quali dati sono contaminati.

Per saperne di più

Find SQL injection vulnerabilities using Burp Suite’s web vulnerability scanner