Buffer Overflow

Indice dei contenuti

    Il buffer overflow (o stack/heap smashing) è una vulnerabilità di sicurezza che si verifica quando un programma scrive più dati in un buffer di quanti ne possano contenere, sovrascrivendo aree di memoria adiacenti. Nei linguaggi senza gestione automatica della memoria (C, C++), questa condizione non genera un’eccezione controllata: il programma scrive silenziosamente in memoria che non dovrebbe toccare. Un attaccante che controlla i dati in eccesso può sfruttare questo comportamento per deviare il flusso di esecuzione del programma verso codice arbitrario — un exploit di buffer overflow può tradursi in Remote Code Execution (RCE) o privilege escalation.

    Meccanismo: stack-based overflow

    Il tipo più classico coinvolge il call stack. Quando una funzione viene chiamata, il frame dello stack contiene (in ordine): i parametri, il return address (l’indirizzo a cui tornare dopo il ritorno dalla funzione), il frame pointer del chiamante e le variabili locali — incluso il buffer vulnerabile. Se il buffer è allocato nello stack e la funzione copia input utente in esso senza verificarne la lunghezza (strcpy, gets, sprintf non bounded), scrivere abbastanza byte sovrascrive il return address. Alla fine della funzione, il processore carica il return address contraffatto e salta all’indirizzo controllato dall’attaccante.

    void leggi_input(char *utente) {
        char buffer[64];
        strcpy(buffer, utente);  /* vulnerabile: nessun controllo sulla lunghezza */
    }

    Se utente contiene più di 64 byte, i byte in eccesso sovrascrivono la memoria oltre il buffer.

    Shellcode e ROP

    Storicamente, il payload iniettato era shellcode: sequenza di istruzioni macchina inserita nel buffer stesso, con il return address modificato a puntare all’inizio del buffer. Le protezioni moderne (NX/DEP, ASLR) hanno reso questa tecnica inefficace nella forma originale.

    La tecnica contemporanea è ROP (Return-Oriented Programming): invece di iniettare codice nuovo, l’attaccante concatena sequenze di istruzioni già presenti nel binario o nelle librerie (gadget) terminanti in RET, costruendo una catena di esecuzione arbitraria senza introdurre nuovo codice.

    Heap overflow

    L’overflow può avvenire anche nello heap (memoria allocata dinamicamente). Lo sfruttamento è più complesso perché il layout dell’heap è meno prevedibile, ma la superficie d’attacco è ampia: molte vulnerabilità nei browser, nei parser di file e nei gestori di protocolli di rete sono heap overflow.

    Difese

    Linguaggi memory-safe: Rust, Go, Java, Python eseguono bounds checking automatico — i buffer overflow non sono possibili per costruzione. La migrazione verso linguaggi memory-safe è la difesa più efficace a lungo termine (vedi NSA, CISA, Microsoft che la raccomandano esplicitamente).

    Stack canary: il compilatore inserisce un valore casuale (canary) tra il buffer e il return address. Prima di eseguire il RET, controlla che il canary non sia stato modificato. Rilevabile da GCC con -fstack-protector.

    NX/DEP (No-Execute / Data Execution Prevention): marca le pagine di dati (stack, heap) come non eseguibili. Impedisce l’esecuzione di shellcode iniettato, ma non blocca ROP.

    ASLR (Address Space Layout Randomization): randomizza gli indirizzi di caricamento di stack, heap e librerie. Rende difficile per l’attaccante conoscere gli indirizzi necessari per il payload. Efficace in combinazione con NX; aggirabile tramite info leak che rivelano l’indirizzo base.

    Safe C functions: usare strncpy, snprintf, fgets al posto delle varianti non bounded. Necessario ma insufficiente — richiede disciplina del programmatore.

    Ultimo aggiornamento: