Il buffer overflow: introduzione

15 December 2008 da Marco

Questo è il primo di tre articoli in cui cercherò di trattare una delle vulnerabilità più gravi che affliggono il software: il buffer overflow. In questo primo articolo spiegherò più o meno approfonditamente cos'è il buffer, lo stack, il bound checking eccetera, ed i principi base che permettono di sfruttare un buffer overflow a vantaggio di un eventuale attaccante.

Introduzione

Va anticipato che uno dei linguaggi di programmazione più predisposti al buffer overflow (d'ora in poi BOF) è senza dubbio il C/C++. Infatti a differenza di altri linguaggi permette al programmatore un controllo quasi completo sulla memoria - quasi quanto un linguaggio assembly – ma non sempre fa gli opportuni controlli, a scapito dei programmatori alle prime armi. Linguaggi come il Java, ad esempio, hanno una gestione molto sofisticata della memoria, ricorrendo ad algoritmi di de-allocazione e a sistemi come il Garbage Collector, quindi i rischi di BOF sono molto rari.
Vediamo innanzi tutto cos'è il buffer. Nella sua accezione più generale un buffer è un'area di memoria contigua, con una dimensione prefissata, utilizzata per memorizzare dati omogenei. Nel linguaggio C un buffer è rappresentato dal puntatore all'indirizzo iniziale dell'area di memoria corrispondente. Alcune funzioni della libreria standard del C (strcpy(), strcat(), gets() e la famosa scanf()) operano su buffer di caratteri senza effettuare nessun controllo sulla dimensione del buffer di destinazione (bound checking): semplicemente arrestano la loro scrittura quando non hanno più caratteri disponibili. È evidente che in questi casi è abbastanza facile incorrere in un buffer overflow. Nel caso in cui il buffer mal gestito sia una variabile automatica, sia cioè allocato sullo stack, la situazione può venire sfruttata da un attaccante per sovrascrivere l'indirizzo di ritorno di una chiamata da funzione e forzare così l'esecuzione di codice malevolo, che può essere posizionato sia nel buffer stesso che in una variabile d'ambiente.
In sintesi un buffer overflow avviene quando in un programma, al tempo di esecuzione, una certa istruzione tenta di scrivere dentro ad un buffer più informazioni di quante esso ne possa contenere.

Organizzazione della memoria

È possibile suddividere la memoria allocata ad un processo in tre zone, ognuna specializzata per una determinata funzione: un'area text, un'area dati statica ed un'area dati dinamica:

Lo stack e sua gestione

Lo stack è quell'area di memoria che si trova sul fondo della memoria e che viene utilizzata durante le chiamate alle funzioni per salvare lo stato corrente dell'esecuzione e per passare i parametri per argomento. Viene gestito con la politica LIFO (Last In, First Out) e mediante due istruzioni fondamentali del linguaggio assembly: PUSH e POP. La prima memorizza un dato, la seconda lo estrae seguendo il modello LIFO, l'ultimo ad entrare è il primo ad uscire. La memorizzazione dei dati nello stack segue un ordine inverso, cioè parte dal fondo e man mano che le informazioni vengono memorizzate, sale verso la cima della memoria.
La gestione dello stack è affidata a due registri a 32-bit molto importanti, chiamati EBP e ESP (base pointer e stack pointer), usati – rispettivamente – per delimitare la cima e il fondo dello stack; quando si esegue una PUSH, il registro ESP viene decrementato di un numero di byte pari alla dimensione del dato memorizzato, quando si esegue una POP il registro ESP viene invece incrementato. Le variazioni di ESP e EBP possono inoltre essere fatte anche direttamente, tramite operazioni di somma e sottrazione (ADD e SUB): ad esempio l'istruzione SUB ESP,5 riserva cinque byte nello stack. Questo tipo di struttura si rivela efficiente e utile nella gestione della gestione delle chiamate di funzioni e procedure in un programma, costituendo quello che è il meccanismo della “call chain”: una funzione può infatti richiamare un'altra funzione che a sua volta può chiamarne un'altra ancora e così via. Il sistema operativo deve poter tener traccia di tutte le chiamate innestate fra loro ed essere in grado di tornare a punti prestabiliti del programma dove richiesto. Ogni chiamata ad una funzione viene tradotta in una istruzione di tipo CALL, che ha l'effetto di congelare l'esecuzione del codice fino a quel punto, passando poi il controllo alla funzione chiamata, dopo aver salvato lo stato corrente e l'indirizzo di ritorno (return address), che servirà per riportare l'esecuzione nel punto in cui si era interrotta (il registro EIP è quello che punta sempre all'istruzione corrente). Da questo meccanismo si evince una prima considerazione importante: poiché l'indirizzo di ritorno viene salvato nello stack, una eventuale corruzione di tale area dati impedirà ad un qualsiasi programma di ritornare in uno stato consistente, con conseguente crash dell'esecuzione.
Per approfondimenti riguardo allo stack si veda il seguente link: Stack on Hacknowledge

Usi malevoli del buffer overflow

Come ho detto ad inizio articolo abbiamo a che fare con un errore particolarmente grave, che permetterebbe ad un hacker di ottenere una shell (anche root in certi casi) sulla macchina vittima.
Detto sinteticamente – comunque nei prossimi articoli vedremo il tutto più nel dettaglio – è possibile far eseguire del codice macchina semplicemente sovrascrivendo l'indirizzo di ritorno e facendolo puntare ad uno shellcode. Quest'ultimo è un “programma” in grado di sfruttare un buffer overflow, e solitamente provoca l'apertura di una shell - per questo shellcode - sulla macchina vittima. Uno shellcode viene solitamente scritto tramite codici esadecimali che identificano le varie istruzioni assembly da eseguire, successivamente questo codice viene iniettato nel buffer e fatto eseguire modificando l'indirizzo di ritorno della funzione vulnerabile. I rischi di un errore del genere in alcuni casi possono essere gravissimi, ad esempio un login che va in buffer overflow se il nome utente è troppo lungo...
Ecco un'immagine che forse vi faciliterà la comprensione di quanto detto:

In ogni caso questo è un overflow dello stack – uno dei più comuni – ma ne esistono anche altri meno conosciuti, ed anche più difficili da sfruttare: heap overflow, frame pointer exception e l'adjacent memory overflow.
L'overflow dello stack rimane il più facile da sfruttare, poiché permette di maneggiare in maniera efficace i registri EBP, ESP e EIP, fondamentali per poter eseguire del codice

Ma vediamo ora com'è possibile prevenire un BOF:

strcpy(buf, ptr); //insicuro
strncpy(buf, sizebuf(buf), ptr); //sicuro

oppure con il classico scanf():


char buf[10];
scanf("%s", buf); //insicuro
scanf("%10s", buf); //sicuro

Nei prossimi due articoli tratterò appunto questo tipo di buffer overflow, applicandolo ai due sistemi operativi più famosi, ovvero Windows e Linux.

SE QUESTO ARTICOLO TI E' SEMBRATO INTERESSANTE CONDIVIDILO O DAGLI UN +1, CONTRIBUIRAI A FAR CRESCERE IL BLOG

Pubblicato in Informatica | Commenti (4)

4 Risposte a “Il buffer overflow: introduzione”

  1. Margherita Hack ha scritto:

    Questa cosa mi ricorda molto le famose stelle collapsar, tra l'altro uno dei miei corpi astronomici preferiti. Non a caso queste stelle collassano da un momento all'altro, proprio come il vostro stack, creando un buco nero in rotazione in grado di assorbire materia ed accelerarla a velocità prossime a quelle della luce. Metaforizzando questa materia potrebbe essere formata dalle istruzioni maligne che voi hackers - questo termine mi è molto familiare - andate ad iniettare nella memoria.
    Che dite, potrebbe starci questo mio traslato?

  2. Marco ha scritto:

    che dire... ha fatto un'osservazione davvero acuta, ma come aspettarsi altrimenti? lei è un'astrofisica con le ovaie, la stimo moltissimo.
    grazie dei commenti ^^

  3. luca ha scritto:

    Hai mai provato con BugFighter, forse può aiutarti.

    Lo uso per individuare i buffer overflow nei vettori mono e multidimensionali.

    Il sito è: http://www.bugfighter-soft.com

    Saluti

    Luca

  4. Door Actuator %0B ha scritto:

    ',~ I am really thankful to this topic because it really gives great information .*,

Leave a Reply