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.