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

Exploiting Authentication and Access Control Mechanisms with Burp Suite

Exploiting Authentication and Access Control Mechanisms with Burp Suite.

In questo articolo ho raccolto in maniera più o meno ordinata alcuni appunti presi durante il relativo corso seguito su Hakin9, alla cui piattaforma rimando per il materiale necessario.

Note sulla Burp Suite Community Edition.

È stata utilizzata la versione Community Edition e attese le limitazioni imposte dalla da tale versione, occorre installare alcune  estensioni. Nel tab Extender/BAppStore cercare e installare “Turbo Intruder” e “JWT Editor”.

In Proxy/HTTPhistory viene mostrata l’interazione con sito: request/response
In Target/Scope aggiungere in “Include in scope” l’url del sito target in modo tale da visualizzare solo quello e non eventualmente altro contenuto web visitato. Quindi in Proxy/HTTPhistory cliccare sulla riga dei filtri e spuntare “Show only in-scope items”.

User Enumeration.

Con Burp Suite professional: clic tasto destro del mouse sulla riga nell’HTTPhistory (host/method/url …) quindi “Send to Intruder” e poi tab Intruder.

Con Burp Suite CE: tasto dx, “Extensions>Turbo Intruder>Send turbo intruder”.

Dopo aver tentato il login con credenziali errate, nella finestra “HTTPhistory” seleziono la riga che ha come metodo “POST” (ed ha anche i parametri) e invio a “Intruder”:

Nella finestra “Raw” di Intruder sostituisco il valore assegnato a “username” con l’operatore %s, al quale verranno passati gli username elencati nella lista “users” mediante il ciclo for:

Dall’elenco prodotto dal risultato è possibile notare che l’utente “cathy” ha i campi “Words” e “Length” differenti dal resto degli username, questo significa che la pagina di login ha risposto in maniera diversa per questo utente, probabilmente perché è un username valido:

Nalla finestra “Raw” assegno a username il nome “cathy”, mentre a password l’operatore %s e modifico il ciclo for in modo tale che a “%s” vengano passati gli elementi della lista “passwords”:

Dall’elenco del risultato è possibile notare che la password “1337ing” ha valore “Word” e “Lengh” differenti, a significare che è la password corretta per l’utente “cathy”:

Infatti, inserendo le credenziali “cathy” e “1337ing”, ottengo l’accesso.

Brute Force Evasion.

Nel tab Proxy ci sono le finestre “Request” e “ Response”, poiché in queste non possiamo scrivere  per modificarne il contenuto (Request) e verificarne l’esito (Response), viene usato il “Repeater”

Tasto dx mouse sulla riga in HTTPhistory e “Send to Repeater” e verrà attivato il relativo tab.

In alto a sinistra compare un numero, che si raccomanda di cambiare per avere traccia delle modifiche con un doppio clic, ad esempio in “Brute-force evasion” se stiamo tentando un attacco brute force.

Uno dei possibili utilizzi è quello di superare il ban temporaneo quando si tenta di fare login per un certo numero di volte con credenziali errate.

Nella finestra “Request” di Repeater si può modificare il codice inserendo ad esempio la riga “X-Forwarded-For: 192.168.12.92” nella quale si fornisce un IP fittizio da modificare raggiunto il numero dei tentativi concessi:

Sulla finestra “Request” tasto dx del muose > “Extensions>Turbo Intruder>Send turbo intruder”, andiamo a modificare l’html sostituendo le ultime cifre dell’IP con l’operatore di formattazione %s e assegnando a username=%s, mentre con lo script di Python li gestiremo mediante un ciclo for:

e così anche per le passwords:

Note: oltre a “X-Forwarded-For:”, “X-Forwarded-By:” “X-Real-IP:”, “X-Forwarded-Host:”


Dopo aver tentato il login con credenziali errate, nella finestra “HTTPhistory” seleziono la riga che ha come metodo “POST” (ed ha anche i parametri) e invio a “Intruder”:

Nella finestra “Raw” di Intruder sostituisco il valore assegnato a “username” con l’operatore %s, al quale verranno passati gli username elencati nella lista “users” mediante il ciclo for:

Purtroppo dopo aver lanciato lo script non ottengo nessuna informazione utile a causa dell’IP-based block protection:

Per aggirare l’ IP-based block protection dalla finestra “HTTPhistory” invio la precedente riga (quella con URL del login e con metodo “POST”) al “Repeater” che mi permetterà di modificare il codice html nella  finestra “Raw” di “Request”. Posso così bypassare la protezione utilizzando l’assegnazione a “X-Real-IP:” di un indirizzo IP fittizio. Poi premo “Send” per inviare la richiesta e poi invio a “Intruder”:

Ora modifico le ultime cifre dell’IP appena inserito con l’operatore %s e il valore assegnato a username sempre con l’operatore %s, in questo modo con il ciclo for posso inviare oltre i valori della lista “users” anche valori numerici (in questo caso in un intervallo da 0 a 100) come ultime cifre dell’indirizzo IP; questo mi permetterà di avere ad ogni richiesta di login un IP differente e quindi bypassare l’ IP-based block protection:

Infatti, ora riesco a bypassare la protezione sull’IP e posso notare che l’utente “anonymous” ha valori “Words” e “Length” differenti dagli altri username:

Modifico script, nella finestra “Raw” assegno a username il nome “anonymous”, mentre a password l’operatore %s e modifico il ciclo for in modo tale che a “%s” vengano passati gli elementi della lista “passwords”:

L’esecuzione dello script mostra che la password “unknown” ha valori “Words” e “Length” differenti dagli altri a significare che abbiamo trovato la password per l’utente “anonymous”:

Utilizzando le credenziali così ottenute, riesco ad eseguire l’accesso correttamente alla pagina.

Wordlist:

MFA Brute Force.

MFA: autenticazione multi fattore. Esempio in cui il login richiede oltre a utente e password anche l’inserimento di un codice:

Dall’analisi del codice della pagina, appuriamo che il l’MFA richiede un codice numerico di 4 cifre e quindi modifico lo script come segue:

L’esecuzione del brute force trova il codice MFA corretto:

Ottenendo così l’accesso.

MFA No Restrictions.

Provo ad accedere alla pagine “/admin”:

e credenziali errate restituiscono “non autorizzato”:

Inserisco le credenziali di login “admin/admin” e mi viene richiesto l’MFA:

Attivo “Repeater”:

Torno all’HTTPhistory:

noto che nel “Request” non ci sono cookie, mentre nel “Response” trovo un “Set-Cookie: session=”, questo sta a significare che il server web comunque sta mantenendo traccia della sessione.

Copio la sessione col cookie:

e incollo nel “Request” del “Repeater” (NB: va incollato nella Raw “Request” selezionando in HTTPhistory un URL prima che si acceda a “/mfa”, cioè in quello di “/login” subito prima):

premo “Send” e nel “Response” avrò “200 OK”:

così ho superato il MFA perchè sono rimasto nella stessa sessione. 
Tasto dx su “Request” e invio al browser “In original session”:

Copio l’url:

e incollo al posto dell’url di richiesta MFA:

e ottengo l’accesso bypassando l’MFA.

Endpoint Enumeration

Accedendo con le corrette credenziali vengo indirizzato alla “dashboard”. Invio la pagina all’Intruder:

Sostituisco l’endpoint “dashboard” con l’operatore di conversione “%s” in modo da iterare i valori di una lista di endpoints:

Ho filtrato i risultati affinché vengano esclusi quelli con status http 404. L’esecuzione dello script mi permette di trovare come payload l’endpoint “.htaccess” e un commento che dice “# Remember to add .htaccess on the /sUp3r_s3cr3t_4dm1n”.

Nell’URL della pagina, sostituisco l’endpoint “dashboard” con “sUp3r_s3cr3t_4dm1n” e trovo il flag.

Origin-Based AC

Invio la pagina con method GET al repeater:

E modifico il parametro di GET al fine di trovare una pagina valida:

Si potrebbe usare un dizionario già pronto, tipo:

Invio il Request al Turbo Intruder e modifico il RAW in modo che attraverso lo script invio i diversi “endpoints”:

Posso migliorare l’output dello script, filtrando le risposte attraverso l’esclusione delle “404 NOT FOUND”, allo scopo usiamo i “DECORATOR”, supportati da Turbo Intruder:

Nella fattispecie, scelgo il “FiltersStatus”:

E lo modifichiamo alla scopo che escluda le pagine “404”, ecco lo script completo:

Quindi “Attack” ed ecco la risposta:

Noto che col suffisso “admin” ho tentato l’accesso ad una pagina alla quale non sono autorizzato, ciò significa che la pagina esiste. Raffino l’iterazione aggiungendo il suffisso “admin”:

Rilancio lo script, ma purtroppo ottengo nuovamente una pagina alla quale non sono autorizzato accedere:

Provo un’altra strada, accedo con le classiche credenziali: hakin9/burpsuite:

Riesco ad accedere ma non ottengo flag:

Allora mi sposto sul tab “Proxy” e noto che vi è una sessione cookie relativa all’accesso appena ottenuto:

Quindi invio al “Repeater”, dove al posto di “dashboard” sostituisco “admin/secret”, clicco su “send” ed ottengo questa volta “Invalid referer”:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer

Poiché “Referer” tiene traccia anche della pagina da dove “provengo”, aggiungo un “Referer” alla pagina “admin” che so che esiste, infatti invio con “Send” e ottengo “200 OK”:

Quindi invio al browser:

Copio ed incollo il link al posto dell’url della pagina e ottengo l’accesso:

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin

RBAC – Role Based Access Control

Invio la pagina di login iniziale all’Intruder:

Quindi aggiungo l’operatore di conversione %s per iterare fra le varie pagine (endpoint enumeration):

In questo caso però ottengo un risultato diverso dai precedenti, intanto un codice di stato 302. Si tratta di un codice status HTTP della pagina web che indica lo spostamento temporaneo della risorsa. Come tutti gli status code 3XX si riferisce a un cambiamento di indirizzo della pagina. Ma mentre il redirect 301 si riferisce a un reindirizzamento permanente – la pagina non esiste più e sposto su un nuovo URL – con la condizione HTTP 302 c’è un cambio momentaneo. Che presuppone un ritorno alla condizione precedente. Possiamo dire che un reindirizzamento 302 è una modifica temporanea che porta gli utenti verso una nuova risorsa per un periodo limitato, fino a quando il reindirizzamento non viene rimosso del tutto.Infatti, come indicato dai “Location”, il reindirizzamento è verso “/m3/v2/”, ovvero la pagina iniziale di login.

Provo a sfruttare questa caratteristica accedendo con le credenziali “hakin9/burpsuite” e una volta stabilita una sessione:

sostituisco “dashboard” con “admin” e ottengo:

… questo mi permette di sfruttare il “roleID”, quindi nvio la pagina al “Repeater”:

quindi “Send”:

Aggiungo dopo “admin” ?roleId= seguito dall’ID che si solito è un numero, per esempio “0” restituisce un “Invalid role ID”:

Invio il tutto all’Intruder:

Quindi programmo un attacco a brurteforce al roleId e filtro escludendo le risposte http con status 401:

ed ecco la risposta:

Ora possio chiedere la relativa sessione:

e copiarla nella pagina di admin, oppure inserire direttamente il roleId individuato:

e trovare il flag.

Insecure Deserialization

La deserializzazione non sicura si verifica quando i dati controllabili dall’utente vengono “deserializzati” da un sito web. Ciò consente potenzialmente a un utente malintenzionato di manipolare oggetti serializzati per passare dati dannosi nel codice dell’applicazione. È persino possibile sostituire un oggetto serializzato con un oggetto di una classe completamente diversa.

Accedo con le classiche credenziali e noto che oltre al cookie di sessione ho quello relativo all’user:

Se ricarico la pagina, trovo quel cookie user anche nel “Request”:

Cerchiamo di capire cosa sono queste stringhe. Invio la pagina all’Intruder, quindi sostituisco l’endpoint “dashboard” con l’operatore di conversione %s al quale passare i valori della lista “endpoints” e filtro i risultati escludendo HTTP Status 404:

Ed ecco il risultato:

mando la stessa pagina al “Repeater”:

Se invio “Send” con endpoint “dashboard”, ovviamente la pagina funziona:

e funziona anche se sostituisco con “admin”, infatti la pagina mi restituisce un http status “UNAUTHORIZED”. Notiamo due cose, però: l’uso del linguaggio di programmazione Python e che il cookie user non è cambiato:

Perciò provo a intervenire su quel cookie utente. Noto che modificando ottengo un 500 INTERNAL SERVER ERROR a causa di una stringa in codifica base64 non valida:

È possibile manipolare le stringhe per operazioni di codifica e decodifica codificate direttamente in Burp Suite attraverso le funzioni chre troviamo nel tab “Decoder”:

Oppure usare la libreria “pickle” di Python per serializzare/deserializzare dato che che l’applicazione usa appunto Python:

decodifico il cookie user:

>>> pickle.loads(base64.b64decode(‘.......’))

infatti restituisce esattemante l’username:

{‘currente_user’: ‘hakin9’}

il risultato lo assegno al coockie:

>>> cookie = pickle.loads(base64.base64decode(‘.......’)) 

Quello che voglio ottenere è la codifica di un altro utente, ovvero “admin”  che possa garantire l’accesso, quindi ne assegno il nome a current_user:

>>> current_user = {‘current_user’: ‘admin’}

>>> base64.b64encode(pickle.dumps(current_user))

ed ecco “admin” codificato in base64:

b ‘....’ 

Quindi copio il valore tra gli apici e lo incollo sostituendo il precedente cookie user  e con “send” posso verificare che la risposta mi da un HTTP 200 OK status:

Invio a browser:

Incollo il link nella pagina di accesso:

E ottengo l’accesso come admin e trovo il flag.

Forging JWT

I token Web JSON (JSON Web Tokens o JWT) sono uno standard aperto (RFC 7519) per la creazione di token di accesso sicuri e compatti. Vengono ampiamente utilizzati per l’autenticazione e l’autorizzazione in applicazioni web e API.

Un token JWT è una stringa codificata che consiste di tre sezioni separate da punti: l’intestazione (header), il payload (contenuto) e la firma (signature). Ognuna di queste sezioni è codificata in Base64 e può essere facilmente decodificata per ottenere i dati contenuti all’interno del token.

L’intestazione contiene informazioni sull’algoritmo di firma utilizzato e il tipo di token. Il payload contiene le informazioni aggiuntive specifiche dell’applicazione, come l’identità dell’utente, i diritti di accesso o altre informazioni pertinenti. La firma viene utilizzata per verificare l’integrità del token e garantire che non sia stato manomesso.

Un aspetto importante dei token JWT è che sono stateless, ovvero tutte le informazioni necessarie per verificare l’autenticità e l’autorizzazione sono contenute nel token stesso. Questo consente ai server di evitare di dover memorizzare lo stato del token o di eseguire query al database per ogni richiesta.

I token JWT offrono diversi vantaggi, tra cui:

  • sicurezza: i token JWT possono essere firmati digitalmente utilizzando algoritmi crittografici, garantendo l’integrità e l’autenticità dei dati;
  • portabilità: i token JWT sono autocontenuti e possono essere trasportati facilmente come stringhe nei campi delle richieste HTTP o come dati in formato JSON;
  • scalabilità: i server possono verificare l’autenticità dei token in modo efficiente senza dover accedere a un database o a una memoria condivisa;
  • estensibilità: è possibile aggiungere campi personalizzati nel payload dei token per includere informazioni aggiuntive specifiche dell’applicazione;
  • interoperabilità: JWT è uno standard ampiamente adottato e supportato da numerosi linguaggi di programmazione e framework.

Tuttavia, è importante notare che i token JWT devono essere utilizzati correttamente e con precauzione. Alcuni aspetti da considerare includono la corretta gestione delle chiavi di firma, la scadenza dei token e l’uso di connessioni sicure per trasmettere i token.

In conclusione, i token JWT offrono un meccanismo flessibile e sicuro per l’autenticazione e l’autorizzazione nelle applicazioni web e API. Consentono l’implementazione di flussi di autenticazione come OAuth 2.0 e forniscono un modo efficiente per trasportare e verificare le informazioni di autenticazione e autorizzazione tra le parti coinvolte.

https://jwt.io/ consente di decodificare, verificare e generare JWT.

Prima di accedere con le consuete credenziali, verifichiamo se sono abilitate le estensioni JWT:

questo consentirà di vedere evidenziate le richieste che utilizzano tali token:

Alcuni siti web usano il JWT per l’autorizzazione:

Se nell’url della pagina sostituisco dashboard con admin ottengo come risultato “Unauthorized”:

Invio allora quest’ultima risposta al repeater e avendo l’estensione JWT abilitata, trovo il tab “JSON Web Token” disponibile:

dove trovo i dettagli del JWT:


Provo a cambiare lo “username” da hakin9 a admin, quindi send  e vediamo cosa succede:

Purtroppo restituisce un errore di “signature” perchè è fallita ovviamente la verifica della firma:

Provo a cancellare la firma:

e riusco a ottenere uno stato HTTP 200:

Quindi chiedo la sessione browser:

che una volta utilizzata mi darà accesso al flag.


Accedo con le consuete credenziali e sostituisco nell’url l’endpoint “dashboard” con “admin”, ma non ottengo l’accesso in quanto non autorizzato. Dall’HTTPhistory invio quest’ultima pagina al “Repeater” quindi apro la scheda “JSON Web Token”:

Cambio il valore di “username” da “hakin9” a “admin”, quindi “Send”, ma ottengo un errore sulla validità della “firma”:

Mi sposto nella scheda “Raw” e provo a eliminare la “firma” e ottengo un altro errore che mi dice che “Algorithm is not set to None”:

Torno alla scheda “JSON Web Token” e cambio il valore di “alg” da “HS256” a “None” ottenendo così un HTTP 200 status OK:

Quindi “Request in browser” > “In original session” e l’url così ottentuto lo incollo sl posto di quello della pagina web e ottengo il flag.

Cracking Keys

Accedo con le solite credenziali, quindi nell’url della pagina sostituisco dashboard con admin e ottengo come risposta “Unauthorized”.
Dal Raw del Request copio il JWT e lo uso con lo script python “jwt_tool.py” (dove -C sta per cracking e -d per indicare quale dizionario da utilizzare):

e ottengo “hidden” come valore di chiave corretto.

Utilizzando jwt.io creo la firma corretta: assegno “admin” a “username” e “hidden” al campo “secret”, quindi copio il JWT così ottenuto:

Torno su Burp Suite e invio la pagina con “admin” al Repeater, quindi sostituisco il JWT presente con quello appena calcolato e verifico con “send”:

Questa volta lo status HTTP è 200 Ok, per cui richiedo la sessione browser:

 quindi incollo url nel browser e ottengo l’accesso.


Effettuato come la solito il login, invio la pagina al Turbo Intruder, quindi sostituisco “dashboard” con l’operatore di conversione “%s” al quale passo la lista degli “endpoint” e filtro i risultati per ottenere solo lo stato HTTP 404:

Il risultato dello script restituisce l’endpoint “admin” e come errore lo stato HTTP 401 UNAUTHORIZED dovuto a “Invalid role for accessing the admin page”:

… quindi devo cambiare il ruolo per l’utente “admin”:

Per fare questo ho bisogno della chiave segreta necessaria per la “firma”, quindi utilizzo il “jwt_tool.py” per recuperarlo utilizzando il JWT dell’utente “hakin9” come input:

Copio il JWT e lo inserisco nel modulo di codifica del sito “jwt.io”:

Quindi cambio il valore di “username” e “role” entrambi in “admin” e inserisco la chiave segreta “123NotAParticularyGreatSecret!!!”:

Mando la pagina al “Repeater” e sostituisco il vecchio JWT con quello nuovo appena creato, poi “Send” e controllo di avere uno stato HTTP 200 OK:

Richiedo la sessione originale nel browser e la incollo nell’url ottenendo così l’accesso voluto.

Implementation Flaws

Accedo con le solite credenziali e sostituisco “dashboard” con “admin” e anche in questo caso risulta che non sono autorizzato. Invio la pagina con “admin” al Repeater e apro il tab “JWT Web Token”, qui posso notare un altro tag nell’header, ovvero il “kid”:

L’attestazione “kid”’ (ID chiave) è un’attestazione facoltativa in un JSON Web Token (JWT) che viene utilizzata per identificare la chiave usata per firmare il JWT. Lo scopo dell’attestazione “kid” è consentire ai destinatari del JWT di determinare quale chiave è stata utilizzata per firmare il token, in modo che possano cercare la chiave pubblica corrispondente e verificare la firma. Ciò è particolarmente utile quando sono in uso più chiavi, ad esempio quando si ruotano le chiavi o si utilizzano chiavi diverse per scopi diversi.

L’attestazione “kid” è inclusa nell’intestazione JWT e il suo valore è una stringa che identifica in modo univoco la chiave usata per firmare il token. Quando il destinatario di un JWT riceve il token, può utilizzare l’attestazione “kid” per cercare la chiave pubblica corrispondente alla chiave utilizzata per firmare il JWT e quindi utilizzare quella chiave pubblica per verificare la firma.

È importante notare che l’attestazione “kid” è facoltativa e non tutti i JWT la includeranno. Tuttavia, è consigliabile includerla quando possibile, in quanto può migliorare notevolmente la sicurezza e la facilità d’uso dei JWT nelle applicazioni.

Una possibile vulnerabilità relativa al “kid” è che a volte può indicare il percorso dove la chiave è memorizzata all’interno del server, infatti se copio il percorso e lo sostituisco ad “admin” nell’url, otteniamo la chiave che ha generato il token:

Copio l’intero JWT e lo incollo sul sito “jwt.io”, quindi modifico con “admin” lo “username” e nel campo “secret” inserisco il valore della chiave che ha generato il JWT:

La firma è stata verivicata, allora posso utilizzare il nuovo JWT così ottenuto al posto di quello presente nel “Raw” del “Repeater”, verifico con “send” e quindi richiedo le sessione browser: 

Utilizzando l’url così ottenuto guadagno l’accesso.

OAuth 2.0 and CSRF

OAuth (Open Authorization) è un protocollo di autorizzazione standardizzato che consente a un utente di concedere l’accesso a una risorsa protetta a un’applicazione di terze parti senza condividere le proprie credenziali di accesso.

In altre parole, OAuth consente a un utente di autorizzare un’applicazione di terze parti (ad esempio, un’applicazione mobile o un’applicazione web) ad accedere alle proprie informazioni senza dover fornire le proprie credenziali di accesso a questa applicazione. OAuth utilizza un token di accesso che viene concesso all’applicazione di terze parti dall’utente, con il quale l’applicazione può accedere alla risorsa protetta.

Questo protocollo di autorizzazione è ampiamente utilizzato per consentire l’accesso a servizi web e API, come i social network (ad esempio Facebook, Twitter, LinkedIn) e i servizi di archiviazione cloud (ad esempio Google Drive, Dropbox). OAuth viene utilizzato anche per la federazione delle identità e per l’autenticazione unica, consentendo all’utente di utilizzare le proprie credenziali di accesso per accedere a più servizi, senza dover effettuare l’accesso separatamente per ciascun servizio.

OAuth è un protocollo aperto e standardizzato, il che significa che può essere utilizzato da qualsiasi sviluppatore per integrare i propri servizi con altri servizi esistenti.

Una volta caricata la pagina di login, notiamo nell’HTTPhistory due diversi host: “h9burpsuite” e “oauth2.h9burpsuite”, quest’ultimo relativo all’autenticazione mediante il protocollo “oauth” appunto:

Sono importanti nel “Raw” i valori relativi a “client_id”,  “response_type=code” e “scope” come visto in precedenza. 
Andata a buon fine l’autenticazione, veniamo reindirizzati alla pagina di “h9burpsuite”, con il nuovo “code” ovvero il token d’accesso:

Infatti se torno alla pagina principale e clicco sul login di ottengo l’accesso senza immettere nuovamente le credenziali.

Vediamo come poter sfruttare l’oauth. Dapprima effettuo il logout, quindi di nuovo login e inserisco le credenziali, ma prima di inviare mi sposto su Burp Suite e dal tab “Intercept” abilito l’ “Intercept on”:

solo a questo punto completo il login inviando le credenziali, vedreò popolarsi la pagina “Raw” dell’ “Intercept”:

A questo punto, sempre nella pagina dell’ “Intercept”, cliccho su “Forward” e interagisco con la pagina web del login, ripeterò le operazioni alcune volte finchè non otterrò una risposta con “code=…”. Quindi tasto destro e copiamo l’URL.:

Ora non andiamo più avanti col “Forward” perchè avrei un reindirizzamento alla pagina precedente, ovvero quella che richiede l’accettazione della mail, ma clicco su “Drop” e interrompo l’ “Intercept”:

Posto su off l’ “Intercept”, mio sposto sulla pagina web e nell’URL cancello tutto quello che riguarda il precedente oauth:

Nella nuova pagina di login, inserisco l’URL precedentemente copiata e ottengo così l’accesso.


Caricata la pagina , dall’http history di Burp Suite, invio l’URL al Turbo Intruder

Usando lo script, itero fra gli endpoints, filtrando i risultati affinché vengano escluse le pagine con http STATUS 404:

Ottengo come risultato positivo l’endpoint “.well-known”:

Invio al Repeater e verifico con “send” ottengo uno stato HTTP 200 OK, ma nessun’altra informazione:

Mediante Turbo Intruder, allora, ripeto nuovamente la stessa operazione sull’endpoint appena trovato:

Ed ottengo un nuovo endpoint positivo:

Nel Repeater verifico con “send” e questa volta ho una risposta positiva ma con maggiori informazioni:

Su quanto ottenuto effettuo “Request in Browser” -> “In original session” e copio l’url:

E lo incollo nel mio browser ottenendo così il flag.

Redirect URI

Nell’ HTTP history è possibile notare il link del “redirect_uri”:

Invio dunque al “Repeater” così da modificare il valore del “redirect_uri” all’interno del “Decoded from”:

Provo a decodificare “google.com”:

Applico le modifiche “Apply changes” e invio con “Send”, il test non ha funzionato perchè viene segnalato un errore di “Invalid URI”:

Questo accade perché il redirect si aspetta “h9burpsuite:4499” all’interno dell’URI. Un modo per farlo è quello di “ancorare” il dominio richiesto all’interno dell’URI mediante il simbolo del “cancelletto” #:

Quindi applico la modifica, “Send” e questa volta ottengo uno status HTTP OK:

Copio l’URI:

Quindi su Proxy-> Intercept, ma senza attivarlo, carico nuovamente la pagina per il login e solo ora, prima di fare login, attivo l’Intercept. Quindi sostituisco il valore dell’URI presente nell’Intercept con quello copiato in precedenza:

Quindi “Forward” e fermo l’Intercept, torno sulla pagina di login, inserisco la credenziali, ma questa volta vengo reindirizzato sulla pagina di google:

Note su OpenID Connect + SAML

OpenID Connect (OIDC) è un protocollo di autenticazione basato su OAuth 2.0, che permette di ottenere informazioni di autenticazione e identità di un utente. OIDC è stato sviluppato per fornire un’alternativa più sicura e interoperabile rispetto all’originale protocollo OpenID.

OIDC consente a un’applicazione web di autenticare l’utente tramite il flusso di OAuth 2.0 e di ricevere un ID Token che contiene informazioni sull’utente, come l’ID dell’utente e il nome. L’ID Token è firmato digitalmente e può essere verificato dall’applicazione per garantire che sia stato emesso da un’autorità di sicurezza attendibile.

Inoltre, OIDC fornisce un endpoint denominato UserInfo che consente alle applicazioni di richiedere informazioni sull’utente come ad esempio l’indirizzo email o la data di nascita.

OIDC è diventato un protocollo popolare per l’autenticazione su Internet ed è stato adottato da molte aziende, come Google, Microsoft, Amazon e molti altri, per consentire l’autenticazione degli utenti sui loro servizi e applicazioni web.

SAML (Security Assertion Markup Language) è un protocollo standard per lo scambio di informazioni di autenticazione e autorizzazione tra diverse applicazioni web. SAML è stato sviluppato per consentire l’accesso sicuro a risorse web attraverso le frontiere organizzative, come ad esempio tra fornitori di servizi e fornitori di identità.

SAML si basa su una struttura di messaggi basata su XML e utilizza la crittografia per garantire la sicurezza delle informazioni di autenticazione e autorizzazione durante la trasmissione. Il protocollo SAML prevede tre ruoli principali:

  • Fornitore di Identità (Identity Provider – IdP): il sistema che autentica l’utente e fornisce un assertion (asserzione) SAML contenente informazioni di autenticazione e autorizzazione;
  • Fornitore di Servizi (Service Provider – SP): il sistema che fornisce la risorsa protetta e che richiede l’autenticazione e l’autorizzazione tramite un assertion SAML;
  • Utente Finale: l’utente che accede alla risorsa protetta.

Il processo di autenticazione e autorizzazione con SAML prevede l’invio di un Assertion SAML dal Fornitore di Identità al Fornitore di Servizi per autorizzare l’accesso all’utente finale. L’Assertion SAML contiene informazioni sulle credenziali di autenticazione dell’utente, come l’ID dell’utente e le autorizzazioni.

SAML è stato adottato da molte organizzazioni come standard per l’autenticazione sicura e l’autorizzazione tra applicazioni web, in particolare in ambito Enterprise.