Buffer Overflow

I Buffer Overflow sono stati la forma più comune di vulnerabilità software degli ultimi 10 anni. Inoltre, le vulnerabilità di questo tipo dominano l’area delle vulnerabilità per l’accesso remoto di rete, dove un utente di internet cerca di guadagnare l’accesso parziale o totale di un host remoto. Se le vulnerabilità di questo tipo potessero effettivamente venir eliminate, una grande porzione di possibili minacce ad un sistema operativo verrebbero meno.
Cerchiamo adesso di descrivere meglio cos’è un Buffer Overflow e vedere le varie tipologie esistenti.

Vulnerabilità Buffer Overflow e Attacchi

L’obiettivo di un attacco Buffer Overflow è quello di sovvertire la funzione di un programma privilegiato in modo che un attaccante possa prendere il controllo di questo programma, e se il programma ha i privilegi sufficienti, controllare l’intero host.

Tipicamente un attaccante mira ad un programma con privilegi di root, e cerca di eseguirci sopra codice simile a “exec(sh)” per ottenere una shell di root. Anche se non sempre è così. Per raggiungere questo obiettivo, prima l’attaccante deve raggiungere due sotto-obiettivi:

1. Rimediare del codice nello spazio d’indirizzamento del programma.

2. Fare in modo che il flusso del programma salti a quel codice, con appositi parametri caricati nei registri e nella memoria.

Categorizziamo un attacco di tipo Buffer Overflow in termini di raggiungimento di questi due obiettivi.

Piazzare il codice malevolo nello spazio d’indirizzamento del programma vittima

Ci sono due modi per piazzare del codice nello spazio d’indirizzamento del programma: iniettandolo oppure usando del codice già presente.

Iniettandolo:

L’attaccante fornisce una stringa come input al programma, che salverà in buffer. La stringa contiene byte che sono istruzioni native per la CPU, compatibili con la piattaforma attaccata. In questo scenario, l’attaccante abusa del buffer del programma attaccato. Alcuni accorgimenti su questo metodo:

– L’attaccante non deve necessariamente effettuare un overflow di un buffer; alcuni Payload potrebbero benissimo essere iniettati in buffer di dimensioni contenute, adibiti per precisi scopi e funzionalità del programma.

– Il buffer può essere localizzato ovunque: sullo stack o sull’heap (variabili allocate dinamicamente).

Codice già presente:

Spesso, il codice per far ciò che l’attaccante vuole è già presente sullo spazio d’indirizzamento del programma. L’accattante ha solo bisogno di parametrizzare il codice, e far in modo che il programma poi salti al codice.
Per esempio, se un attaccante volesse eseguire un “exec(‘/bin/sh’)”, ma già esiste il codice nelle libc che esegue un “exec(arg)”, dove arg è un puntatore a stringa. L’attaccante avrà solo bisogno di cambiare l’argomento facendolo puntare a “/bin/sh”. Questo è quel che si intende per parametrizzare la funzione.

Indurre il programma (bersagliato) a saltare al codice malevolo

Il metodo base è quello di fare l’overflow di un Buffer senza (o con controlli deboli) controlli sulla lunghezza dell’input. Per corrompere parte dello stato del programma (ad esempio puntatori adiacenti al buffer).
Facendo l’overflow del buffer, l’attaccante fuoriesce dallo spazio allocato per il buffer e va a sovrascrivere le sequenze di byte adiacenti al buffer. Alterando l’intera logica del programma.

Ecco un’immagine che meglio rende il concetto:

strcpy_buffer_overflow

La classificazione qui, viene fatta in base al tipo di stato che il buffer overflow cerca di corrompere.
Potrebbero essere stati del tipo:

Activation Records:

La forma più comune di buffer overflow consiste proprio di un’iniezione di codice e la modifica del flusso del programma, corrompendo un Activation Record. Ogni volta che una funzione viene chiamata, stabilisce un activation record sullo stack.
Questo activation record, contiene, fra le tante cose, anche un indirizzo di ritorno a cui saltare quando la funzione termina.
Corrompendo l’indirizzo di ritorno nell’activation record di una funzione, l’attaccante fa si che il programma salti al codice iniettato, quando la funzione termina. Questa forma di Buffer Overflow si chiama “Stack smashing attack”.

Function Pointers:

“void (* foo)()” dichiara la variabile foo che è del tipo: puntatore a funzione che restituisce void.
I Puntatori a funzione possono essere allocati ovunque (stack, heap, static data area) e quindi l’attaccante ha solo bisogno di trovare un buffer adiacente ad un puntatore a funzione. Quindi effettuare l’overflow del buffer e modificare il puntatore. Dopo un pò, quando il programma farà una chiamata attraverso quel puntatore, farà un salto alla posizione immessa dall’attaccante: solitamente un codice malevolo.

Esempio di codice C vulnerabile

Qui di seguito un codice C vulnerabile ad un attacco di tipo Buffer Overflow:

void function(char *str) {
char buffer[16];
strcpy(buffer,str);
//......
}
 
void main() {
char large_string[256];
int i;
for( i = 0; i < 255; i++) large_string[i] = 'A';
function(large_string);
}

Da notare che la funzione usa strcpy() invece di strncpy(), che è una funzione che non controlla la lunghezza dell’input!

Soluzioni al Buffer Overflow

Quella del Buffer Overflow, è una tecnica che colpisce solitamente programmi scritti in C o in C++. In particolare quei programmi che usano vecchie funzioni per allocare memoria, e che in automatico non effettuano alcun controllo sullo spazio allocato e l’effettiva lunghezza dell’input passato dall’utente. Ad esempio: strcpy, gets, sprintf etc…

Tra le soluzioni più efficaci ad un buffer overflow, oltre a quelle scontate, di utilizzare funzioni che controllino la lunghezza degli input, ve ne sono anche di più drastiche:

– Stack non eseguibile.
– Modifiche al compilatore

Che però risultano attualmente poco attuabili. Perchè molti programmi standard richiedono l’esecuzione di routine sullo stack, per sistemi di ottimizzazione e quant’altro. Oppure nel secondo caso, modifiche al compilatore minano alle performance del programma.

Parte delle informazioni sono state tratte e tradotte dal Paper scientifico “Buffer Overflows: Attack and Defenses for the Vulnerability of the Decade” di Crispin Cowan, Perry Wagle, Calton Pu, Steve Beattie e Jonathan Walpole.

Torna all'inizio