Buffer overflow sullo stack
Un caso molto particolare e interessante per i bug di tipo buffer overflow è quando il buffer si trova sullo stack.
Infatti, all'interno dello stack ci sono alcune informazioni interessanti, quali le altre variabili locali della funzione, altri metadati per il record di attivazione e l'indirizzo di ritorno, ovvero l'indirizzo dell'istruzione a cui la funzione deve saltare quando ritorna.
Questo è tipicamente implementato tramite l'istruzione ret
, che fa una operazione di pop dallo stack, e salta all'indirizzo letto.
Ad esempio prendiamo la seguente funzione c.
#include <stdio.h>
int main() {
char name[32];
scanf("%64s", name);
printf("ciao %s\n", name);
}
Il record di attivazione della funzione main
è composto dal buffer name
, e poco più in basso si trova l'indirizzo di ritorno della funzione main
.
Una vulnerabilità di tipo buffer overflow (che è presente nel programma) ci potrebbe permettere di sovrascrivere questo indirizzo di ritorno e saltare a un indirizzo arbitrario di memoria di nostro controllo.
Infatti, inserendo un input più grande di 32 bytes, è possibile andare a sovrascrivere questo indirizzo di ritorno e causare un segmentation fault.
$ ./main
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
ciao AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
[1] 1258251 segmentation fault (core dumped) ./main
Tramite un debugger come gdb
è possibile verificare che il programma ha tentato di eseguire una ret
all'indirizzo 0x4141414141414141
(corrispondente a otto byte di A
), ma questo non è mappato nel processo, causando un segmentation fault.
0x55555555518d <main+68> ret <0x4141414141414141>
Manipolando opportunamente l'input è possibile far saltare il programma a un indirizzo specifico, prendendo così il controllo del flusso di esecuzione e portando nella maggior parte dei casi alla possibilità di eseguire codice arbitrario.