Corruzione dello Heap
L'allocazione dinamica della memoria in C e C++ viene gestita manualmente, attraverso l'uso di funzioni come malloc
e free
in C, e operatori come new
e delete
in C++.
Ogni richiesta di memoria all'allocatore ci restituisce un puntatore, e una regione di memoria grande quanto richiesta.
L'allocatore assume implicitamente che il programma utilizzerà solo quella porzione di memoria, e non andrà a scrivere in altre porzioni limitrofe.
Se questo dovesse accadere, si ha una istanza di corruzione della memoria dell'heap, e nonostante non sia facile da sfruttare come ad esempio un buffer overflow sullo stack, può nel peggiore dei casi portare a esecuzione di codice arbitrario.
Inoltre, la chiamata a free
libera il blocco, e ci possono essere delle corruzioni anche derivanti dal fatto che il blocco di memoria sia utilizzato anche dopo la chiamata free
, oppure che sia liberato più volte.
Vediamo adesso i bug più comuni che portano alla corruzione della memoria dell'heap.
Buffer overflow
Anche in questo caso, viene allocato un buffer, e una cattiva gestione dei limiti di questo buffer può portare alla scrittura in zone limitrofe. Infatti, spesso le regioni di memoria allocate sono limitrofe, e potremmo riuscire a scrivere in un buffer limitrofo. Questo però non è l'unico impatto che può avere un buffer overflow. Infatti l'allocatore, in particolare quello di GNU libc, la libreria standard più usata nei sistemi Linux, utilizza alcune porzioni di memoria tra i blocchi allocati per mantenere dei metadati, utilizzati ad esempio per sapere quanto è grande il prossimo blocco allocato. La scrittura di questi metadati può forzare l'allocatore dei comportamenti inaspettati, e nel peggiore dei casi può portare all'esecuzione di codice arbitrario.
Use after free (UAF)
Questo bug deriva dal fatto che stiamo utilizzando un puntatore non più valido (un cosiddetto dangling pointer) perché la memoria è già stata liberata. L'allocatore quando viene liberato un blocco scrive al suo interno dei metadati che verranno utilizzati per delle sue logiche interne di gestione della memoria libera. L'allocatore assume quindi che quella regione di memoria non verrà più sovrascritta dal programma, ma ovviamente non c'è nessun meccanismo di prevenzione della scrittura, quindi un bug di tipo use after free potrebbe andare a corrompere questi metadati.
Double free
Questo bug si verifica quando viene chiamata per due (o più) volte la funzione free
sullo stesso puntatore.
Anche in questo caso, possiamo andare a forzare dei comportamenti inaspettati nella logica dell'allocatore.
Come sfruttare queste vulnerabilità
Queste tipologie di bug sono sfruttabili solo avendo una conoscenza profonda di come funziona internamente l'allocatore, e in generale non è banale ottenere esecuzione arbitraria. Inoltre, siccome sono dipendenti dalla logica dell'allocatore, dipendono anche dalla versione della libreria standard che stiamo usando. Tuttavia, questa tipologia di bug è molto comune nei software moderni e, come detto in precedenza, il fatto che un bug sia difficile da sfruttare non significa che sia impossibile. Ci sono svariate tecniche che si possono trovare per sfruttare questi bug, ad esempio una raccolta si può trovare a questa pagina, e l'argomento è anche d'interesse accademico per capire come rendere più sicure le implementazioni degli allocatori. Infatti col passare del tempo sono state inserite parecchie mitigazioni nella libreria standard per le vulnerabilità comuni, che rendono più difficile sfruttare questi bug.