Una vulnerabilità 0-day affligge il protocollo HTTP/2. Si chiama Rapid Reset ed è molto pericolosa!

1

Preambolo

Per questo articolo dovremo addentrarci in alcuni aspetti tecnici del protocollo HTTP/2, pertanto abbiamo fatto un lungo preambolo per permettere a tutti di comprendere l’argomento.

Della vulnerabilità in sé daremo una spiegazione semplificata, sempre per lo stesso motivo.

Chi fosse interessato ai dettagli può leggere il post apposito sul blog di Cloudflare.

Brevissima storia di HTTP

HTTP è la base su cui si fonda internet. Acronimo di HyperText Transport Protocol fa parte della rivoluzione WWW di più 30 anni fa, ed è il protocollo di trasferimento per il contenuto delle pagine scritte in HTML (HyperText Markup Language).

Lo stesso Tim Berners-Lee, il creatore del protocollo, ha presentato nel 1996 come standard HTTP versione 1.0, che già l’anno dopo diventava HTTP 1.1 (rivisto ogni tanto fino al 1999). Ma sostanzialmente il protocollo non è più cambiato nei successivi vent’anni, quando l’esplosione di internet ha richiesto un adeguamento per poter sfruttare meglio le risorse disponibili.

HTTP/2 è quell’adeguamento. Di fatto evoluzione di una ricerca avviata da Google (progetto SPDY), ne conserva caratteristiche base, come:

  • l’impostazione client server;
  • l’uso del TCP come protocollo di trasporto;
  • l’uso di testo semplice (ASCII) per il contenuto;
  • l’uso di comandi (GET e POST, per fare degli esempi).

Quindi, l’architettura delle applicazioni che fanno uso di HTTP e delle sue caratteristiche non viene toccata. Sono invece client e server a dover implementare il protocollo con tutte le nuove caratteristiche, e in particolare parliamo del multiplexing, il vero vantaggio.

HTTP 1.1 normalmente instaura una connessione TCP per ogni richiesta. Ma, ogni connessione TCP richiede una fase di negoziazione, che risulta in tempo sprecato – e overhead nell’uso dei dati – per l’utente finale.

È però prevista anche la possibilità di fare serializzazione delle richieste sulla stessa connessione (una delle novità di HTTP 1.1), ma in questo caso il limite è dato dal fatto che il server è tenuto a rispondere in ordine e completamente alle richieste ricevute dal client. Il quale non può interrompere (se lo ritiene necessario o anche solo utile) l’operazione. O meglio: può chiudere la connessione ed aprirne un’altra, ma torniamo al problema di tempo (e overhead) che abbiamo citato poco fa.

HTTP/2 ovvia a tutto questo impostando la comunicazione su una connessione in una serie di stream paralleli: ogni richiesta è associata ad uno stream, che è indipendente dagli altri.
Questo permette al client di fare le richieste in parallelo, e al server di rispondere (sempre in parallelo) nell’ordine che gli è più comodo. Inoltre, il client può interrompere una richiesta (già in esecuzione o ancora in attesa).

In questa maniera i tempi morti si riducono di molto, e vengono trasferite solo le informazioni utili – quelle non più necessarie sono fermate prima.

La vulnerabilità

La parallelizzazione delle richieste però è un vettore perfetto per gli attacchi Denial of Service (DoS), ovvero caricare di richieste il server così tanto da non riuscire più a rispondere a nessuna – negando quindi il sevizio a tutti.

Se poi la cosa diventa Distribuited DoS (DDoS) il tutto non può che peggiorare.

Per questo il protocollo HTTP/2 prevede un numero massimo di richieste contemporanee. Ma nel conto rientrano solo quelle a cui il server ha risposto o che il client non ha annullato.

Ecco l’inghippo: se il client fa una richiesta, e subito dopo la annulla (reset), quella richiesta è come se non fosse mai esistita – non conta. Quindi per il client è assolutamente lecito fare una nuova richiesta, che può annullare immediatamente dopo. Da qui il nome della tecnica: Rapid Reset.
Il tutto, magari, ancora prima che il server abbia ricevuto le richieste.

La mole di traffico che viene ad abbattersi sul server è limitata solo dalla banda disponibile tra client e server – e nei DDoS questa è sempre a favore del client, ovvero dell’attaccante.

Il problema potrebbe essere tutto qui: saturazione di banda in ricezione per il server, che – vedendo tutte le richieste annullate – non invierà nulla. Quindi, al massimo fastidio, giusto?

Beh, mica tanto.

Il server accoda le richieste prima di elaborarle, ovvero di sapere che sono da buttare via. Inoltre, la richiesta comunque c’è, cosa che farà preparare la risposta al server, che nel migliore dei casi interromperà quando vedrà l’annullamento della richiesta.

Inoltre, la gran parte delle architetture evolute prevede più strati di gestione e ottimizzazione/resilienza prima di arrivare al server vero e proprio: bilanciatori, WAF, reverse proxy. Ognuno di questi elementi può inoltrare quella richiesta all’elemento successivo prima di vedere l’annullamento, impegnando ulteriormente risorse per una risposta che verrà prodotta e buttata via.

Quindi, per quanto poche, delle risorse sono impegnate ad ogni richiesta, con potenziale riverbero su più strati di sistemi. E se le richieste sono centinaia di milioni al secondo, le risorse necessarie per fare niente possono diventare semplicemente troppe – arrivando al DoS.

Cloudflare ha registrato un picco a 201 milioni di richieste per secondo, generate da una botnet di 20 mila client: sono numeri di molto oltre il consueto, soprattutto per la piccola dimensione della botnet, a dimostrazione che la minaccia è seria.

Per di più il problema è a livello di protocollo: ogni server che implementa HTTP/2 ne è (almeno sulla carta) vulnerabile.

Mitigazioni

Un modo possibile è lavorare sulle impostazioni del server abbassando il più possibile il numero di stream contemporanei (non sotto i 100, molti browser si aspettano almeno questa disponibilità). In questo caso si penalizzano le prestazioni (ma di poco), rendendo più difficile la vita agli attaccanti (sempre, purtroppo, di poco). E, a proposito, questo quanto sostiene NGINX:

By relying on the default keepalive limit, NGINX prevents this type of attack. Creating additional connections to circumvent this limit exposes bad actors via standard layer 4 monitoring and alerting tools.

Grazie al limite di keepalive predefinito su cui si basa, NGINX impedisce questo tipo di attacco. La creazione di connessioni aggiuntive per eludere questo limite espone gli attori malintenzionati attraverso gli strumenti standard di monitoraggio e segnalazione del livello 4.

Il che non vuol dire essere al sicuro, ma che è possibile avere un allarme fin dalle prime fasi di attacco. Sempre che si abbia un sistema di monitoraggio che controlla certi parametri (in particolare, le risposte con codice 499).

Proprio perché il problema è di protocollo, o si interviene lì (prevedendo un minimo di tempo tra richiesta e annullamento, o un massimo di reset per connessione) o non c’è particolare soluzione: possiamo solo sperare in patch dei server che implementino una qualche gestione interna per mitigare questo tipo di attacco. Ma, alla data attuale, non siamo a conoscenza di tali implementazioni.

Però, HTTP 1.1 non è vulnerabile a questa tecnica. Quindi, se proprio necessario (magari perché già sotto attacco), una soluzione possibile è disattivare HTTP/2 dai propri servizi – a costo di prestazioni più basse (talvolta parecchio).

Ho coltivato la mia passione per l’informatica fin da bambino, coi primi programmi BASIC. In età adulta mi sono avvicinato a Linux ed alla programmazione C, per poi interessarmi di reti. Infine, il mio hobby è diventato anche il mio lavoro.
Per me il modo migliore di imparare è fare, e per questo devo utilizzare le tecnologie che ritengo interessanti; a questo scopo, il mondo opensource offre gli strumenti perfetti.