SSRF e OAuth exploitation: attacchi alla logica applicativa che il WAF non vede

SSRF e OAuth exploitation: attacchi alla logica applicativa che il WAF non vede

C'è una categoria di attacchi che un firewall applicativo, il WAF, non vede passare, e non perché sia configurato male: per costruzione. Il WAF è bravissimo a riconoscere pattern di attacco noti, una stringa di SQL injection, uno script di cross-site scripting, un payload che ha la forma del male. Ma gli attacchi alla logica applicativa non hanno la forma del male: usano richieste che sembrano perfettamente legittime, perché lo sono, e abusano del modo in cui l'applicazione le interpreta. Due dei vettori più gravi e più sottovalutati appartengono a questa categoria: la Server-Side Request Forgery e l'abuso dei flussi OAuth. In un'attività di penetration testing, sono tra le catene che portano più lontano, perché il cliente si sente protetto dal suo WAF e non si rende conto che quel WAF non sta nemmeno guardando il piano su cui l'attacco avviene. In questo articolo spiego come funzionano davvero questi due attacchi, perché il perimetro non li ferma, e dove vivono le difese che invece li chiudono: nel codice, non nel firewall.

Perché un WAF non ferma questi attacchi?

Perché il WAF filtra ciò che ha la forma di un attacco, e questi attacchi hanno la forma di una richiesta normale. È la distinzione cruciale. Un WAF lavora ispezionando il traffico in ingresso alla ricerca di firme malevole: sa che una certa sequenza di caratteri è tipica di una SQL injection, che un certo tag è tipico di uno script malevolo, e li blocca. Funziona perché quegli attacchi hanno una sintassi riconoscibile. Gli attacchi alla logica applicativa, invece, non hanno una sintassi malevola: una SSRF è semplicemente un URL messo in un campo dove l'applicazione si aspetta un URL; un abuso di OAuth è semplicemente un parametro di redirect con un valore che il WAF non ha modo di sapere essere sbagliato.

Il WAF non conosce la logica di business dell'applicazione, e quindi non può sapere che quel particolare URL punta a un servizio interno che non dovrebbe essere raggiungibile, o che quel redirect porta a un dominio dell'attaccante invece che a uno legittimo. Per il WAF sono richieste sintatticamente valide, e le lascia passare. Questo è il motivo per cui affidarsi al perimetro per difendersi da questi attacchi è un'illusione di sicurezza: il perimetro guarda la forma, l'attacco vive nel significato. La difesa deve stare dove il significato è conosciuto, cioè nell'applicazione stessa, che è la sola a sapere quali destinazioni e quali redirect sono legittimi.

Un WAF blocca ciò che assomiglia a un attacco. Gli attacchi alla logica applicativa non assomigliano a niente di sospetto: assomigliano all'uso normale. È per questo che il firewall li lascia passare e la difesa deve vivere nel codice, l'unico posto dove si sa cosa è legittimo e cosa no.

Come funziona la Server-Side Request Forgery?

Funziona convincendo l'applicazione a fare una richiesta di rete, per conto dell'attaccante, verso una destinazione che l'attaccante sceglie. Molte applicazioni hanno bisogno di fare richieste in uscita: recuperare l'anteprima di un link, validare un webhook, scaricare un'immagine da un URL fornito dall'utente, chiamare un'API esterna. Quando l'applicazione prende un URL fornito dall'utente e ci fa una richiesta senza validare dove punta, si apre la SSRF: l'attaccante fornisce un URL che non punta a una risorsa esterna legittima, ma a qualcosa che lui non potrebbe raggiungere da fuori e che invece l'applicazione, dall'interno della rete, raggiunge.

Il bersaglio tipico sono i servizi interni: database, pannelli di amministrazione, microservizi che, fidandosi di essere accessibili solo dalla rete interna, non hanno autenticazione. L'attaccante, attraverso la SSRF, usa l'applicazione come ponte per raggiungerli. È un esempio perfetto di come una funzionalità innocua, fare una richiesta a un URL, diventi un'arma quando manca la validazione. Ho descritto un caso reale di questa dinamica in un contesto Laravel nell'articolo su come trovare e sfruttare una SSRF con il client HTTP e i webhook, dove il percorso dal form vulnerabile all'intranet del cliente era banale e invisibile dai tool standard.

Perché la SSRF è devastante negli ambienti cloud?

Perché nel cloud esiste un endpoint interno che, se raggiunto, consegna le credenziali della macchina, e la SSRF è esattamente il modo per raggiungerlo. Le piattaforme cloud espongono, all'interno di ogni istanza, un servizio di metadati a un indirizzo IP speciale, non instradabile dall'esterno, da cui l'istanza recupera la propria configurazione e, soprattutto, le credenziali temporanee associate al suo ruolo. Quel servizio si fida di chiunque lo interroghi dall'interno dell'istanza, perché si presume che solo codice legittimo giri lì. Una SSRF rompe questa presunzione: l'attaccante fa puntare la richiesta dell'applicazione all'endpoint dei metadati, e si fa restituire le credenziali cloud dell'istanza.

Da quel momento l'attaccante non è più dentro un container o un'applicazione: ha le chiavi del cloud, con i permessi del ruolo associato alla macchina, e può muoversi nell'infrastruttura come se fosse l'istanza stessa. È la trasformazione di una vulnerabilità applicativa in una compromissione dell'infrastruttura, in pochi passaggi e senza che il WAF veda nulla di anomalo, perché tutto ciò che è successo è che l'applicazione ha fatto una richiesta HTTP, la sua attività più normale. Questo è il motivo per cui la SSRF è una delle vulnerabilità più gravi nel cloud moderno: il suo impatto non è limitato all'applicazione, scala all'intero ambiente. Se vuoi una valutazione di quali delle tue applicazioni espongono questo vettore, nel mio profilo professionale trovi l'esperienza concreta su sicurezza offensiva e cloud per le PMI.

Come si abusa di un flusso OAuth?

Lo si abusa sfruttando i punti in cui il flusso si fida di un valore controllabile dall'attaccante, e il più classico è la gestione del redirect_uri. Il flusso OAuth, alla base di "accedi con Google" e di innumerevoli integrazioni, funziona reindirizzando l'utente a un fornitore di identità che, dopo l'autenticazione, lo rimanda all'applicazione con un codice o un token. Il punto delicato è proprio quel "lo rimanda all'applicazione": l'indirizzo a cui rimandare è specificato da un parametro, il redirect_uri, e se l'applicazione o il fornitore non lo validano in modo rigoroso, l'attaccante può manipolarlo.

La confusione del redirect funziona così: l'attaccante costruisce un flusso in cui il redirect_uri punta a un dominio che controlla lui. Se la validazione è lasca, per esempio se accetta qualunque URL che contenga il dominio legittimo come sottostringa, o qualunque sottodominio, il fornitore di identità rimanda il codice o il token all'indirizzo dell'attaccante, che così ruba l'autorizzazione della vittima. Il risultato è un furto di token: l'attaccante ottiene le credenziali di accesso della vittima all'applicazione, senza mai conoscerne la password. Altri abusi sfruttano la mancanza del parametro anti-falsificazione che lega la richiesta alla sessione dell'utente, permettendo di agganciare la sessione della vittima a un account dell'attaccante o viceversa. In tutti i casi, il WAF non vede nulla: sono richieste OAuth perfettamente formate, solo con i valori sbagliati nei punti giusti.

Come si concatenano questi vettori in un attacco reale?

Si concatenano perché ognuno apre la porta del successivo, e una catena di passi singolarmente piccoli porta molto più lontano della somma delle sue parti. È il modo in cui questi attacchi diventano davvero pericolosi, e vale la pena seguirne uno per intero. Immagina un'applicazione con una funzione apparentemente innocua: genera l'anteprima di un link che l'utente incolla. Un attaccante fornisce, al posto di un link normale, l'indirizzo dell'endpoint dei metadati cloud. L'applicazione, ignara, fa la richiesta e restituisce nell'anteprima il contenuto di quella pagina: le credenziali temporanee dell'istanza.

Da lì la catena prosegue. Con quelle credenziali, l'attaccante interroga i servizi cloud con i permessi del ruolo dell'istanza, e scopre cosa quel ruolo può fare: leggere un bucket di storage, accedere a un database, assumere un altro ruolo con più privilegi. Ogni passo usa l'accesso ottenuto al precedente per estenderlo, e in poche mosse si passa da "ho incollato un link in un form" a "controllo l'infrastruttura cloud". In nessun punto di questa catena il WAF ha visto qualcosa di anomalo: ha visto un'anteprima di link, una richiesta HTTP, una chiamata API autenticata con credenziali valide. Ogni singolo passo era legittimo nella forma; solo l'intenzione e l'incatenamento erano malevoli. Questo è esattamente ciò che rende gli attacchi alla logica così difficili da intercettare con strumenti che guardano le singole richieste: il male non è in nessuna richiesta presa da sola, è nella sequenza.

La stessa logica vale per OAuth concatenato ad altro: un furto di token via redirect malevolo dà all'attaccante l'identità della vittima nell'applicazione, e da quell'identità si parte per accedere a tutto ciò che la vittima poteva fare, magari raggiungendo funzioni amministrative se la vittima era un amministratore. La lezione difensiva di queste catene è che non esiste un singolo punto da blindare: serve spezzare la catena in più punti, perché ogni anello chiuso interrompe il percorso. Bloccare la SSRF chiude il primo anello; limitare i permessi del ruolo dell'istanza riduce cosa l'anello successivo può fare; validare i redirect chiude la porta OAuth. La sicurezza in profondità non è uno slogan, è il riconoscimento che gli attacchi reali sono catene, e che spezzarle in più punti è più robusto che fidarsi di un'unica barriera.

Come si testa la propria esposizione a questi vettori?

Si testa adottando la prospettiva dell'attaccante in modo sistematico, perché questi vettori non emergono da una scansione automatica ma da chi ragiona sulla logica. Il primo passo è inventariare tutti i punti in cui l'applicazione fa una richiesta in uscita verso una destinazione che dipende, anche indirettamente, da un input dell'utente: anteprime di link, webhook, importazioni da URL, integrazioni. Ognuno di questi è un candidato SSRF da verificare, provando a far puntare la richiesta verso indirizzi interni e verso l'endpoint dei metadati e osservando se l'applicazione obbedisce.

Il secondo passo è esaminare ogni flusso di autenticazione e autorizzazione che usa OAuth o reindirizzamenti, verificando come viene validato il redirect_uri e se è possibile manipolarlo, se il parametro anti-falsificazione è presente e controllato, se i token sono verificati come emessi per la propria applicazione. Questi controlli non li fa un tool premendo un bottone: richiedono di capire il flusso e provare a romperlo nei punti di fiducia. È esattamente il tipo di analisi che un penetration test mirato fa e che una scansione automatica salta, ed è il motivo per cui, per le applicazioni che trattano dati di valore, l'occhio umano avversario resta insostituibile. Verificare la propria esposizione prima che lo faccia un attaccante è la differenza tra scoprire questi vettori in un report e scoprirli in una violazione.

Dove vivono davvero le difese?

Vivono nel codice dell'applicazione, dove si conosce la differenza tra una destinazione legittima e una illecita, e si esprimono come validazione rigorosa invece che come filtro di pattern. Per la SSRF, la difesa fondamentale è l'allowlist delle destinazioni: invece di accettare qualunque URL e provare a bloccare quelli cattivi, cosa impossibile da fare in modo completo, si definisce esplicitamente l'insieme ristretto di destinazioni che l'applicazione ha davvero bisogno di raggiungere, e si rifiuta tutto il resto. È l'approccio raccomandato anche dalla guida di riferimento del settore, la cheat sheet OWASP sulla SSRF. A questo si aggiunge il blocco esplicito degli indirizzi interni e dell'endpoint dei metadati cloud, la risoluzione e validazione dell'URL prima della richiesta per evitare che un redirect lo porti altrove, e idealmente l'isolamento delle richieste in uscita, facendole passare da un componente con accesso di rete limitato, così che anche una SSRF riuscita non raggiunga nulla di sensibile.

Per OAuth, la difesa è la validazione esatta del redirect_uri: il fornitore e l'applicazione devono confrontare l'indirizzo di reindirizzamento con una lista di valori esatti pre-registrati, mai con un confronto parziale, mai accettando sottodomini arbitrari o sottostringhe. A questo si aggiungono l'uso rigoroso del parametro anti-falsificazione che lega ogni flusso alla sessione, e la verifica che il token sia stato emesso davvero per la propria applicazione. Sono difese che il perimetro non può fornire, perché richiedono di sapere cosa è legittimo, e quella conoscenza vive solo nel codice.

Il filo che lega questi due attacchi è che entrambi sfruttano la fiducia mal riposta dell'applicazione in un valore fornito dall'esterno, un URL, un redirect, e che entrambi sono invisibili al firewall perché non hanno la forma di un attacco, hanno la forma dell'uso normale. La lezione di sicurezza è che il perimetro è necessario ma non sufficiente: difende dalla forma del male, non dal significato. Gli attacchi più gravi del web moderno non sfondano il muro, ci passano attraverso usando la porta nel modo per cui è stata costruita, ma con intenzioni che il muro non può conoscere. La sola difesa efficace è progettare l'applicazione perché non si fidi ciecamente di ciò che riceve, validando destinazioni e redirect contro ciò che è esplicitamente lecito. Pensare come un attaccante, chiedersi "di quale valore si fida questa applicazione, e cosa succede se glielo controllo io?", è esattamente ciò che porta alla luce questi vettori prima che lo faccia qualcuno con intenzioni peggiori. Se vuoi sapere quali delle tue applicazioni si fidano troppo di ciò che ricevono, contattami per una consulenza diretta: le guardiamo dalla parte di chi le attaccherebbe, e chiudiamo le porte che il tuo firewall non sta nemmeno controllando.

Ultima modifica: