Diventa Autore per CoreTech | Scopri di più
23/03/20 CoreTech Blog
Si verifica una vulnerabilità di overflow del buffer quando si forniscono a un programma troppi dati. I dati in eccesso danneggiano lo spazio vicino in memoria e possono alterare altri dati. Di conseguenza, il programma potrebbe segnalare un errore o comportarsi diversamente. Tali vulnerabilità sono anche chiamate sovraccarico del buffer.
Alcuni linguaggi di programmazione sono più suscettibili ai problemi di overflow del buffer, come C e C ++. Questo perché si tratta di linguaggi di basso livello che si affidano allo sviluppatore per allocare memoria. I linguaggi più comuni usati sul web come PHP, Java, JavaScript o Python, sono molto meno inclini agli exploit buffer overflow perché gestiscono l'allocazione di memoria per conto dello sviluppatore. Tuttavia, non sono completamente sicuri: alcuni consentono la manipolazione diretta della memoria e spesso usano funzioni core scritte in C / C ++.
Le vulnerabilità di overflow del buffer sono difficili da trovare e sfruttare. Inoltre non sono così comuni come altre vulnerabilità. Tuttavia, gli attacchi di buffer overflow possono avere conseguenze molto gravi. Tali attacchi spesso consentono all'attaccante di ottenere l'accesso alla shell e quindi il pieno controllo del sistema operativo. Anche se l'attaccante non riesce ad ottenere l'accesso alla shell, gli attacchi di buffer overflow potrebbero interrompere l'esecuzione dei programmi e, di conseguenza, causare un Denial of Service.
Esistono due tipi principali di vulnerabilità di overflow del buffer: overflow dello stack e overflow dell'heap.
Nel caso di overflow del buffer dello stack, il problema si applica allo stack, che è lo spazio di memoria utilizzato dal sistema operativo principalmente per memorizzare le variabili locali e gli indirizzi di ritorno delle funzioni. I dati sullo stack vengono archiviati e recuperati in modo organizzato (last-in-first-out ), l'allocazione dello stack è gestita dal sistema operativo e l'accesso allo stack è rapido.
In caso di overflow del buffer dell'heap, il problema si applica all'heap, che è lo spazio di memoria utilizzato per archiviare i dati dinamici. La quantità di memoria che deve essere riservata viene decisa in fase di esecuzione ed è gestita dal programma, non dal sistema operativo. L'accesso all'heap è più lento ma lo spazio sull'heap è limitato solo dalle dimensioni della memoria virtuale.
In un semplice programma, è possibile che l'utente inserisca un indirizzo di posta elettronica. Pertanto, si crea una variabile stringa. Si allocano 64 byte alla variabile perché non si prevede che una stringa di posta elettronica sia più lunga di 64 caratteri. Tuttavia, si considera troppo attendibile l'input dell'utente e non si verifica se la lunghezza della stringa immessa supera la dimensione del buffer.
Di conseguenza, l'utente immette 100 caratteri e i restanti 36 caratteri vengono archiviati nella memoria allocata a un'altra variabile. Questo fa sì che il valore di quella variabile cambi e cambi anche il comportamento del programma. Nella maggior parte dei casi, ciò porta a un semplice errore di segmentazione della memoria, ma può avere conseguenze più gravi. Per capire come ciò possa influenzare l'esecuzione del programma, dovremo supporre che la vulnerabilità sia uno stack overflow e che appaia in un programma C.
Un programma C utilizza lo stack per memorizzare un set di dati per ogni funzione. Il set di dati è denominato stack frame e include l'identificatore di funzione, i valori delle variabili locali e l'indirizzo di ritorno. Ecco un semplice esempio di codice sorgente per spiegare come funziona lo stack:
main() {
int mv1;
int mv2;
func();
}
void func() {
int fv1;
int fv2;
}
Quando si esegue il programma, inizia con la main()
funzione. Il programma memorizza i valori delle main()variabili di funzione nella parte superiore dello stack ( mv1
e mv2
). Quindi la main()
funzione chiama la func()
funzione e memorizza i valori delle sue variabili nella parte superiore dello stack ( fv1
e fv2
). Al func()
termine dell'esecuzione della funzione, la parte superiore dello stack viene dimenticata, la funzione corrente torna a main()e il programma ha accesso mv1
e mv2
nuovamente.
Il contenuto dannoso che l'attaccante invia a un programma difettoso è generalmente composto da tre parti:
NOP
istruzione NOP
byteQuando nel nostro esempio si verifica l'overflow del buffer, il programma passa alla catena di NOP
byte (anziché tornare alla main()
funzione). I NOP
byte vengono ignorati e il programma incontra il codice shell nel mezzo di essi. Il codice shell esegue una shell del sistema operativo, dando all'autore dell'attacco il pieno accesso al sistema.
Ecco un esempio molto semplice di un programma C che è vulnerabile a un overflow dello stack:
main(int argc, char *argv[]) {
func(argv[1]);
}
void func(char *v) {
char buffer[10];
strcpy(buffer, v);
}
La strcpy
funzione nell'esempio precedente copia l'argomento del comando nella variabile del buffer di destinazione senza controllare la lunghezza della stringa. Il programma alloca solo 10 byte alla buffer
stringa e quindi strcpy
provoca un overflow del buffer. Se compiliamo questo programma come vulnprog , la seguente chiamata da riga di comando è innocua:
$ vulnprog AAAAAAAAAA
Tuttavia, la seguente chiamata provoca un overflow del buffer:
$ vulnprog AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Come prevenire l'overflow di un buffer
Prevenire gli errori di overflow del buffer non è molto diverso dalla prevenzione di molte altre vulnerabilità. Tutto si riduce a diffidare dell'input dell'utente. Nel caso di vulnerabilità di overflow del buffer, lo sviluppatore deve verificare la lunghezza dell'input prima di utilizzare qualsiasi funzione che potrebbe causare un overflow.
Tuttavia, errare è umano e non è raro che gli sviluppatori dimentichino questa regola di base. Anche i revisori del codice potrebbero perdere tali errori. Ecco perché il metodo di base più sicura in C è quello di evitare i seguenti cinque funzioni non sicure che possono portare ad un Buffer Overflow: printf
, sprintf
, strcat
, strcpy
, e gets
.
Sfortunatamente, il linguaggio C di base fornisce una sola alternativa sicura: fgets
(da utilizzare invece di gets
). Diverse piattaforme hanno implementazioni non standard. Ad esempio, la versione Microsoft di C include sprintf_s
, strcpy_s
e strcat_s
. Sui sistemi Linux/UNIX, la scelta migliore consiste nel vietare le funzioni non sicure e imporre l'uso della libreria C sicura.
È inoltre possibile proteggersi dagli overflow del buffer utilizzando un'estensione di un compilatore che utilizza canarini. I canarini sono valori speciali che il compilatore inserisce nello stack tra la posizione del buffer e la posizione dei dati del controllo. Quando si verifica un overflow del buffer, è il canarino ad essere danneggiato per primo e questo danneggiamento può essere rilevato immediatamente. Ci sono molte estensioni del compilatore che utilizzano canarini, per esempio, StackGuard e ProPolice.
Affinché un overflow del buffer sia possibile, l'utente malintenzionato deve sapere esattamente dove si troverà il buffer nella memoria del computer. In passato, questo era semplice come l'esecuzione di un debugger sul computer locale e il controllo degli indirizzi di memoria. Gli attuali sistemi operativi lo rendono molto più difficile.
Tutti i sistemi operativi moderni includono un meccanismo di protezione chiamato ASLR (Address Space Layout Randomization). Grazie a questo meccanismo, il file eseguibile può essere caricato in molte posizioni di memoria diverse. Pertanto, l'utente malintenzionato non può prevedere facilmente l'indirizzo di memoria a cui passare e molti tentativi di attacco di overflow del buffer falliscono.
Un'altra tecnica che aiuta a prevenire gli attacchi di overflow del buffer è la protezione dello spazio eseguibile (su Windows: prevenzione dell'esecuzione dei dati – Protezione esecuzione programmi). Grazie a questa tecnica, l'utente malintenzionato non può eseguire il codice se si trova nello spazio di memoria assegnato allo stack o heap e, in alcuni casi, anche in altre aree. Questo rende impossibile chiamare direttamente un shell code, ma gli aggressori possono utilizzare trucchi avanzati come la programmazione orientata al ritorno.
Tuttavia, un utente malintenzionato potrebbe tentare di eludere entrambi questi meccanismi di protezione su architetture x86 utilizzando un attacco ret2reg. Quello che devono fare è trovare un modulo (DLL) che non è protetto da ASLR o PROTEZIONE esecuzione programmi. Se in tale modulo è possibile trovare un'istruzione JMP ESP
(salto allo stack, combinazione di byte, FF \FF\E4
), è possibile utilizzare il percorso di questa istruzione come indirizzo di ritorno. Il programma salterà a questa posizione, eseguirà l'istruzione di salto (JMP ESP
) e salterà alla posizione corrente dello stack, che si trova subito dopo l'indirizzo di ritorno (prima del codice shell).
Le applicazioni Web e le pagine Web sono raramente soggette a vulnerabilità di overflow del buffer perché non sono scritte in C o in C++. Tuttavia, questi errori si verificano in software sottostanti, ad esempio server web, server applicazioni web o interpreti.
Lo scanner di vulnerabilità web Acunetix controlla tali errori nel software web e l'integrazione con OpenVAS consente di espandere l'elenco dei controlli per includere il software relativo alla rete. Fai una demo e scopri di più su come eseguire scansioni sul tuo server web.