Back to Posts

Share this post

Heartbleed Bug

Posted by: voidsec

Reading Time: 7 minutes

Pochi giorni fa è stato divulgato il bug Heartbleed, vulnerabilità che affligge la libreria OpenSSL; da quel giorno molte parole sono state spese per cercare di spiegare la falla anche al personale non tecnico, ma molte volte su grandi testate giornalistiche sono state scritte, passatemi il termine, idiozie (“virus scassinatore della rete”).

Scriviamo pertanto questo articolo cercando di analizzare tutta la questione Heartbleed da un punto di vista tecnico.

OpenSSL, libreria utilizzata per criptare le comunicazioni in internet, è il cuore dei protocolli cifrati, come ad esempio SSL/TLS (alla base di HTTPS), ssh, sftp che garantiscono la sicurezza delle comunicazione e la privacy su Internet per applicazioni web, email , instant messaging (IM) e alcune reti private virtuali (VPN) .

La falla (CVE-2014-0160) esiste da due anni, dal dicembre 2011, ed è stata fixata in questi giorni con la versione OpenSSL 1.0.1g. Le versioni vulnerabili di OpenSSL vanno dalla 1.0.1 alla 1.0.1f, mentre non sono afflitte le versioni OpenSSL antecedenti alla 1.0.0 e quelle successive alla 1.0.1g.

Background

La vulnerabilità è stata introdotta in seguito all’implementazione dell’estensione Heartbeat per alcuni protocolli usati da OpenSSL. Hearbeat evita di dover rinegoziare ogni volta la connessione sicura tra client e server, mantenendo vivi i collegamenti grazie a richieste effettuate a intervalli regolari; in particolare il client invia una stringa al server e quest’ultimo la spedisce come risposta al client stesso.

La falla

La falla è stata individuata indipendentemente da un ingegnere di Google, Neel Mehta, e da alcuni ricercatori di Codenomicon; questi ultimi hanno creato un sito ad hoc, appunto Heartbleed.

Il bug consente a un utente malintenzionato di leggere fino a 64 KB di memoria dal sistema vulnerabile, con la possibilità di compromettere le chiavi segrete utilizzate per identificare i fornitori dei servizi e per crittografare il traffico, i nomi e le password degli utenti. Ciò consente agli aggressori di spiare le comunicazioni, di impersonare o rubare i dati direttamente dai servizi e dagli utenti.
Un attacco che sfrutta questa falla non lascia traccia e può essere portato a termine senza l’utilizzo di credenziali né, di particolari privilegi.

Grazie a uno sviluppatore Italiano, Filippo Valsorda, è possibile eseguire un rapido test sui servizi e sulle infrastrutture utilizzate, il test è disponibile quiGitHub.

heartbleed

Ma com’è potuto succedere? Leggiamo il codice e scopriamolo.

Il bug

Il fulcro della vulnerabilità è in ssl/d1_both.c, in particolare all’interno della funzione:

dtls1_process_heartbeat:
int
dtls1_process_heartbeat(SSL *s)
{
unsigned char *p = &s->s3->rrec.data[0], *pl;
unsigned short hbtype;
unsigned int payload;
unsigned int padding = 16; /* Use minimum padding */

In primo luogo si ottiene un puntatore ai dati di un record SSLv3 assegnato a p.
La struttura del record, in cui sono evidenti tipo, lunghezza e puntatore ai dati, è la seguente:

typedef struct ssl3_record_st
{
int type; /* type of record */
unsigned int length; /* How many bytes available */
unsigned int off; /* read/write offset into 'buf' */
unsigned char *data; /* pointer to the record data */
unsigned char *input; /* where the decode bytes are */
unsigned char *comp; /* only used with decompression - malloc()ed */
unsigned long epoch; /* epoch number, needed by DTLS1 */
unsigned char seq_num[8]; /* sequence number, needed by DTLS1 */
} SSL3_RECORD;

Tornando a dtls1_process_heartbeat, viene acquisita la struttura del record assegnato a p, memorizzando il tipo, la lunghezza e l’indirizzo di memoria dei dati.

/* Read type and payload length first */
hbtype = *p++;
n2s(p, payload);
pl = p

Il primo byte del record SSLv3 è il tipo Heartbeat.
La macro n2s legge due byte da p, li scrive in payload, e incrementa il puntatore p, in pratica viene memorizzata la lunghezza. Si noti che la dimensione effettiva nel record SSLv3 non viene controllata.
Infine il puntatore pl contiene l’indirizzo di memoria nel quale sono salvati i dati Heartbeat forniti dal richiedente.

unsigned char *buffer, *bp;
int r;
/* Allocate memory for the response, size is 1 byte * message type, plus 2 bytes payload length, plus * payload, plus padding */ buffer = OPENSSL_malloc(1 + 2 + payload + padding); bp = buffer;

In questo passaggio viene allocata la quantità di memoria richiesta tramite la funzione OPENSSL_malloc: fino a 65535 +1 +2 +16 byte, per essere precisi.
La variabile bp è il puntatore utilizzato per accedere a questa memoria.

/* Enter response type, length and copy payload */
*bp++ = TLS1_HB_RESPONSE;
s2n(payload, bp);
memcpy(bp, pl, payload);

La macro s2n agisce all’inverso di n2s, copia i due byte contenuti in payoad nel buffer bp.
Infine sul buffer viene copiato un quantitativo di dati, presenti all’indirizzo di memoria pl, pari alla lunghezza richiesta dal payload.

Successivamente la funzione si occuperà di inviare il buffer all’utente e di “liberare” la memoria, concludendo l’heartbeat.

Quindi, dov’è il bug?

All’interno della funzione analizzata non viene effettuato un controllo di congruenza sui dati che vengono richiesti dall’utente. Il client nel momento in cui invia una stringa al server per completare un heartbeat, può dichiarare una lunghezza superiore, in questa maniera il server risponderà con dei dati presenti in memoria che non dovrebbero normalmente essere accessibili agli utenti.

Un esempio vale più di mille parole, pertanto immaginiamo che l’utente richieda al server la stringa “ciao”, dichiarando che questa sia lunga 4 lettere; il server scrive all’interno della variabile payload il valore 4, pl punterà alla prima lettera della parola “ciao” e nel buffer di risposta bp copierà solo 4 lettere a partire da quella puntata da pl, e risponderà “ciao”. Adesso immaginiamo che l’utente richieda al server la stringa “hack” e che dichiari che sia lunga 20 lettere; il server memorizzerà all’interno della variabile payload il valore 20, credendo erroneamente che la parola che dovrà inviare come risposta sia lunga 20 caratteri. In bp verrà quindi scritto “hack” seguito dai 16 caratteri successivi presenti in memoria.

Questo accade perché la lettura dal memcpy legge tutta la memoria contigua al record SSLv3 all’interno dello stesso processo. E a quanto pare, c’è un sacco di roba nelle vicinanze.

Si sarebbe potuto evitare?

Al di la del bug presente nel codice, il problema risiede nel modo in cui la memoria viene gestita, ci sono due modi in cui la memoria viene allocata dinamicamente con malloc (almeno su Linux): utilizzando sbrk e utilizzando mmap . Se la memoria viene allocata con sbrk , usa la vecchia regola heap-grow-up e limita ciò che può essere trovato con questo bug, anche se più richieste (specialmente se effettuate contemporaneamente) potrebbero ancora rivelare qualche dato.

In particolare OpenSSL usa un proprio metodo per allocare la memoria anziché quello del sistema, gestendo una propria lista di ciò che è stato già assegnato e successivamente liberato; se fossero state usate le funzioni malloc e free della libreria libc, probabilmente questo bug non sarebbe rimasto attivo così a lungo. La maggior parte delle implementazioni della libc effettuano dei controlli più accurati e esistono dei programmi, come Valgrind, che avrebbero quindi potuto rilevare il problema in maniera automatica.

Il Fix

La parte sostanziale della patch è questa:

/* Read type and payload length first */
if (1 + 2 + 16 > s->s3->rrec.length)
return 0; /* silently discard */
hbtype = *p++;
n2s(p, payload);
if (1 + 2 + payload + 16 > s->s3->rrec.length)
return 0; /* silently discard per RFC 6520 sec. 4 */
pl = p;

La prima parte ferma i “battiti cardiaci” di lunghezza zero, mentre la seconda verifica che la lunghezza del record attuale sia sufficientemente lunga.

Conclusione

Ci piacerebbe ora riflettere un attimo su questa vulnerabilità, non tanto dal punto di vista tecnico ma da un punto di vista culturale:

  1. Sembra assurdo che applicazione critiche siano mantenute da sole quattro persone, che per quanto brave possano essere, non sviluppano le stesse come lavoro primario.
  2. La poca comprensione delle tematiche ha fatto si che gli sviluppatori siano stati “additati” e che il loro lavoro sia stato denigrato, dimenticandosi che si tratta di un progetto open source, scritto nel loro tempo libero che ci ha permesso di mantenere sicure le nostre connessioni e i nostri dati in maniera completamente gratuita.
  3. E’ necessario che grandi colossi, quali Google, Facebook, ecc, che usano gratuitamente queste infrastrutture critiche, investano in controlli di sicurezza, non solo per i loro interessi, ma anche per aiutare la comunità che gli fornisce questi servizi.

di VoidSec e kalup

Back to Posts