Buffer overflow: differenze tra le versioni
Contenuto cancellato Contenuto aggiunto
Aggiunto sinonimo buffer overrun |
Aggiunta sezione su memory segmentation e rivista la parte su stack overflow |
||
Riga 21:
Non tutti i programmi sono vulnerabili a questo tipo di inconveniente.
Per i linguaggi di basso livello, come l’assembly, i dati sono semplici array di byte, memorizzati in registri o in memoria centrale: la corretta interpretazione di questi dati (indirizzi, interi, caratteri, istruzioni ecc…) è affidata alle funzioni e alle istruzioni che li accedono e manipolano; utilizzando linguaggi di basso livello si ha dunque un maggiore controllo delle risorse della macchina, ma è richiesta una maggiore attenzione in fase di programmazione in modo da assicurare l’integrità dei dati (e quindi evitare fenomeni come il buffer overflow). I linguaggi di più alto livello, come il Java e il Python (e molti altri), che definiscono invece il concetto di tipo di una variabile e che definiscono un insieme di operazioni permesse a seconda della tipologia, non soffrono di vulnerabilità come il buffer overflow, perché non consentono di memorizzare in un buffer una quantità maggiore di dati rispetto alla sua dimensione. Fra questi due estremi si trova il linguaggio C che presenta alcune delle astrazioni tipiche dei linguaggi di alto livello insieme a elementi tipici dei linguaggi di basso livello, come la possibilità di accedere e manipolare indirizzi di memoria: ciò rende il linguaggio suscettibile ad usi inappropriati della memoria; se a questo si unisce il fatto che alcune librerie di funzioni molto diffuse (in particolare per l’input e la manipolazione di stringhe come la gets() ) non effettuano un corretto controllo della dimensione dei buffer su cui lavorano, e che il C è stato usato negli anni ’70 per scrivere il sistema operativo UNIX (e da questo sono poi derivati i sistemi come Linux) e molte delle applicazioni pensate per eseguire su di esso, ne consegue che ancora oggi è presente e circola una grande quantità di codice vulnerabile al buffer overflow. <ref name=":0">{{Cita libro|autore=William Stallings, Lawrie Brown|titolo=Computer Security - Principles and Practice|anno=2015|editore=Pearson|città=|p=|pp=|ISBN=978-0-133-77392-7}}</ref>
==
Il buffer overflow può essere indicato con diversi nomi a seconda della posizione occupata dal buffer all’interno della memoria allocata per il processo.
La posizione del buffer è importante in quanto gli effetti del buffer overflow sono principalmente legati a:
== Heap overflow ==▼
* cosa c’è vicino al buffer
* quali dati vanno a sovrascrivere le aree di memoria adiacenti al buffer
=== Gestione e allocazione della memoria virtuale per un processo in esecuzione ===
Quando viene eseguito un programma il sistema operativo normalmente genera un nuovo processo e alloca in memoria centrale uno spazio di memoria virtuale riservato al processo stesso.
Questo spazio di memoria in generale ha una struttura data da (partendo dall’alto verso il basso):
* Kernel
* [[Stack]] (cresce verso il basso)
* Memoria libera
* [[Heap]] (cresce verso l’alto)
* Dati globali
* Codice del programma
L’esecuzione del programma consiste a sua volta di diverse chiamate a funzioni: ciascuna chiamata genera uno stack frame all’interno dello stack (che man mano cresce verso il basso nella struttura descritta sopra, con politica [[LIFO]]); all’interno del frame la funzione chiamata memorizza le variabili locali, l’indirizzo dell’istruzione della funzione chiamante a cui dovrà restituire il controllo (return address) e il puntatore al frame della funzione chiamante; questi ultimi due in particolare giocano un ruolo fondamentale nell’assicurare il giusto flusso di esecuzione al programma fra una chiamata di funzione e l’altra, infatti:
* Il return address dice alla funzione chiamata a quale istruzione della funzione chiamante bisogna restituire il controllo;
* Il puntatore al frame della funzione chiamante consente di ripristinare il suo contesto di esecuzione prima di restituirle il controllo;
Lo stack cresce verso il basso ad ogni chiamata di funzione, e ciascun frame generato presenta dunque una struttura del tipo (sempre dall’alto verso il basso):
{| class="wikitable"
|Return address
|-
|Puntatore al frame della funzione chiamante
|-
|Variabile locale 1
|-
|Variabile locale 2
|-
|...
|}
<ref name=":0" />
=== Stack Buffer Overflow ===
Quando il buffer è allocato nello stack, ovvero è una variabile locale di una funzione, l’eventuale immissione all’interno del buffer di una quantità di dati superiore alla sua portata prende il nome di '''stack buffer overflow''' (o '''stack smashing''', o '''stack-based buffer overflow''').
In questo caso i dati adiacenti al buffer sono il return address e il frame pointer: al termine dell’esecuzione la funzione tenta di restituire il controllo all’istruzione puntata dal return address che, se è stato sovrascritto dai dati in eccesso del buffer, può contenere:
* L’indirizzo di un’area di memoria non accessibile: i dati in eccesso sono casuali, il programma va in crash restituendo tipicamente un [[Errore di segmentazione|segmentation fault]]. E’ un esempio di come lo stack buffer overflow può essere utilizzato come attacco del tipo [[Denial of Service|denial-of-service]] (DoS), compromettendo la disponibilità del servizio colpito.
* Un indirizzo di memoria ben preciso: i dati in eccesso sono calcolati in modo da sovrascrivere il return address con l’indirizzo di un’area di memoria a cui l’attaccante vuole avere accesso, o con l’indirizzo in cui si trova il codice che l’attaccante vuole eseguire.
E’ importante non confondere [[stack overflow]] e stack buffer overflow: il primo indica una situazione per cui si richiede una quantità troppo elevata di memoria nello stack, il secondo una situazione in cui (per varie ragioni) si inserisce in un buffer nello stack una quantità di dati più grande della capacità del buffer stesso.<ref>{{Cita libro|autore=Jon Erickson|titolo=Hacking - The Art of Exploitation|anno=2008|editore=No Starch Press|città=|p=|pp=|ISBN=978-1-59327-144-2}}</ref>
▲=== Heap overflow ===
L'heap overflow avviene quando vi è un eccesso di dati in ingresso nell'area [[heap]] della memoria. Solitamente i cracker generano volutamente degli heap overflow per perforare programmi scritti in modo non impeccabile.
|