INsicurezze/ L’insostenibile leggerezza del software
La scoperta di Heartbleed, uno dei bug più potenzialmente devastanti della storia, suscita riflessioni non solo sulla vulnerabilità dei sistemi. Ma anche sui modelli di sviluppo del software “mission critical”
Il bug denominato Heartbleed, formalmente identificato mediante il codice CVE-2014-0160 con cui è catalogato nel database Common Vulnerabilities and Exposures, tecnicamente non è altro che un classico e banale caso di “memory out of bound”, ossia di accesso ad un’area di memoria indebita a causa di un indice inizializzato male ed in assenza di controlli specifici atti ad impedirlo. Un errore insidioso ma tra i più comuni, specie tra i programmatori principianti, favorito dal fatto che linguaggi come il C per motivi di efficienza non effettuano automaticamente a run-time il controllo del superamento dei limiti.Nella fattispecie il bug venne introdotto all’interno della libreria OpenSSL nel lontano 2011 quando tale Robin Seggelmann, all’epoca dottorando all’Università di Duisburg-Essen in Germania, implementò la cosiddetta estensione Heartbeat nel protocollo SSL/TLS. Questa, che letteralmente significa “battito cardiaco”, era stata proposta alla IETF (Internet Engineering Task Force) proprio da Seggelmann ed altri, e sarebbe stata da lì a poco emanata come standard formale di Internet col nome di “Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS) Heartbeat Extension” (RFC 6520, febbraio 2012). Essa consiste essenzialmente in uno scambio continuo di speciali pacchetti fra client e server autenticati, allo scopo di mantenere attiva e verificata una sessione sicura senza dover rinegoziare ogni volta la connessione.
Il bug nel codice di Seggelmann si trova nella funzione invocata per costruire il pacchetto di Heartbeat da scambiarsi fra client e server, e consiste nel non verificare la validità del valore memorizzato in una variabile, usata come parametro, che dovrebbe contenere la lunghezza del pacchetto stesso. In pratica alla funzione vanno passati come parametri sia il contenuto da assegnare al pacchetto (sotto forma di un array di caratteri) che la lunghezza del pacchetto stesso, ma la mancanza di validazione fa sì che la lunghezza del pacchetto creata dalla funzione possa essere diversa, ed in particolare molto maggiore, di quella dell’array che ne costituirà il contenuto effettivo. Una dimenticanza apparentemente banale ma profondamente pericolosa, come vedremo tra un attimo.
Seggelmann inviò dunque il suo codice per revisione a Stephen N. Henson, uno dei quattro sviluppatori principali di OpenSSL, il quale apparentemente non notò il problema ed incluse il tutto nel repository dei sorgenti della libreria il 31 dicembre 2011. Il codice incriminato entrò quindi a far parte della versione ufficiale 1.0.1 di OpenSSL rilasciata pubblicamente il 14 marzo 2012, e subito adottata da un numero enorme di siti e di distribuzioni di sistemi operativi. Anche le successive release minori, fino alla 1.0.1f, contengono tutte il bug, il quale è stato corretto solo nella versione 1.0.1g rilasciata il 7 aprile 2014 in seguito alla scoperta del problema da parte di alcuni ricercatori indipendenti (Riku, Antti and Matti dell’azienda finlandese Codenomicon e Neel Mehta di Google Security). Per la cronaca, il nome Heartbleed (sanguinamento del cuore) con cui il problema è oggi noto era stato assegnato informalmente al bug dai ricercatori di Codenomicon come gioco di parole su Heartbeat (battito del cuore), ma è subito diventato il nome “ufficiale” con cui esso è universalmente identificato.
Chiarito qual è il bug, vediamo come sia possibile sfruttarlo per carpire informazioni ad un server vulnerabile. Purtroppo è molto facile: basta infatti semplicemente richiedere la restituzione di un pacchetto Heartbeat creato assegnando valori opportunamente malformati ai parametri che ne regolano la costruzione. In pratica occorre passare alla funzione una stringa di inizializzazione corta (ad esempio “pippo”) ed un valore esageratamente ampio come descrizione della sua lunghezza (ad esempio 1.000 o più), ciò che corrisponde a chiedere al server di restituirci un pacchetto costituito dalla stringa “pippo” di mille caratteri! A questa richiesta apparentemente insensata e contraddittoria il server, in mancanza di una specifica validazione, non obietta ma reagisce stolidamente allocando in memoria un buffer di mille caratteri, copiandovi la stringa “pippo” e rimandando indietro tutti e mille i caratteri.
Otterremo così in risposta dal server un pacchetto di mille caratteri nel quale i primi cinque sono la striunga “pippo” e i rimanenti 995 caratteri contengono fedelmente i valori che erano contenuti nella particolare area di memoria del server nella quale è avvenuta l’allocazione del buffer!
In pratica con questo espediente riusciamo ad ottenere dal server la copia di una parte della sua memoria di lavoro, che può in realtà essere di ben 64K per volta; e, trattandosi di memoria dello spazio dati usato dall’istanza attiva del codice OpenSSL, è molto probabile che contenga informazioni significative per il processo stesso, quali ad esempio la chiave privata del server o le credenziali dei client utilizzate nelle sessioni aperte o comunque recenti. Nulla vieta inoltre di inviare centinaia o migliaia di richieste analoghe, per ricostruire così a colpi di 64K alla volta aree assai ampie della memoria del server ed aumentare le probabilità di trovarvi informazioni utili.
In pratica, a furia di richieste ripetute, un client malevolo può riuscire a mapparsi completamente o almeno in buona parte lo spazio di memoria logicamente allocato dal server al processo relativo all’istanza attiva di OpenSSL, di fatto rivelando in chiaro ogni e qualsiasi dato ad esso collegato: comprese quindi le chiavi private e/o le password degli utenti. Un “leakage” di proporzioni colossali, e soprattutto attuabile con facilità e senza lasciare alcuna traccia.
Prove realmente svolte da diversi ricercatori hanno confermato in modo evidente che, effettuando alcune migliaia di richieste, è effettivamente assai probabile riuscire ad ottenere password in chiaro ed altre informazioni critiche dall’analisi dei segmenti di memoria ottenuti. Il problema è infatti aggravato dal fatto che la libreria OpenSSL, per allocare memoria, non utilizza le classiche chiamate standard di sistema quali malloc(), ma implementa delle proprie routine, cosa che comporta due effetti collaterali sfavorevoli: il primo è che così facendo si massimizzano le probabilità che la memoria allocata sia stata precedentemente utilizzata dallo stesso processo, e contenga quindi informazioni sensibili per il suo funzionamento e soprattutto “fresche”; il secondo è che si vanificano gli effetti di eventuali azioni mitigatrici di “sanitarizzazione” della memoria svolte dagli allocatori di sistema, i quali non vengono affatto invocati.
Occorre quindi forse ripensare l’intero modello di trust del codice open source ad elevata criticità, introducendo step formali di “quality assurance” come quelli presenti nei prodotti industriali. Il solo fatto che un pezzo di codice “possa essere esaminato” da sviluppatori indipendenti non dà alcuna garanzia che esso sia stato realmente esaminato. Cullarsi in un falso senso di sicurezza è peggio che non avere sicurezza, lo sappiamo. Porre fiducia nell’open source è cosa buona e giusta, ma dobbiamo avere la certezza che tale fiducia sia ben riposta se non vogliamo trovarci di fronte a problemi sempre più gravi.
E la domanda a questo punto sorge spontanea: quanti altri pezzi di codice open source critico, cui affidiamo quotidianamente la nostra sicurezza, potrebbero portare in sé da tempi immemorabili bug potenzialmente devastanti? E soprattutto, chi dovrebbe accorgersene prima che lo facciano quelli sbagliati?
Corrado “NightGaunt” Giustozzi
nightgaunt.org
fonte: http://punto-informatico.it/4033843/PI/Commenti/insicurezze-insostenibile-leggerezza-del-software.aspx