Vulnerabilità legata alla terminazione stringa
Dopo aver riflettuto su questo aspetto, l'utilizzo di strncpy()
è probabilmente il modo più comune (che posso immaginare) che possa creare errori di terminazione nulli. Poiché generalmente le persone pensano alla lunghezza del buffer come non compreso strncpy(a, "0123456789abcdef", sizeof(a));
. Quindi vedrai qualcosa di simile al seguente:
a
Supponendo che char a[16]
sia inizializzato con a
la stringa strlen(a)
non sarà terminata con null. Quindi, perché questo è un problema? Bene, in memoria ora hai qualcosa come:
30 31 32 33 34 35 36 37 38 39 61 62 63 64 65 66
e0 f3 3f 5a 9f 1c ff 94 49 8a 9e f5 3a 5b 64 8e
Senza un terminatore null le funzioni di stringa standard non conoscono la lunghezza del buffer. Ad esempio, 0x00
continuerà a contare finché non raggiunge un string.h
byte. Quando è questo, chi lo sa? Ma ogni volta che lo trova restituirà una lunghezza molto più grande del buffer; diciamo 78. Vediamo un esempio:
int main(int argc, char **argv) {
char a[16];
strncpy(a, "0123456789abcdef", sizeof(a));
... lots of code passes, functions are called...
... we finally come back to array a ...
do_something_with_a(a);
}
void do_something_with_a(char *a) {
int a_len = 0;
char new_array[16];
// Don't know what the length of the 'a' string is, but it's a string so lets use strlen()!
a_len = strlen(a);
// Gonna munge the 'a' string, so lets copy it first into new_array
strncpy(new_array, a, a_len);
}
Hai appena scritto 78 byte su una variabile alla quale sono assegnati solo 16 byte.
Buffer Overflow
Si verifica un overflow del buffer quando vengono scritti più dati su un buffer rispetto a quelli allocati per quel buffer. Non è diverso per una stringa, tranne per il fatto che molte delle funzioni a_len
fanno affidamento su questo byte null per segnalare la fine di una stringa. Come abbiamo visto sopra.
Nell'esempio abbiamo scritto 78 byte in un buffer che è solo assegnato per 16. Non solo, ma è una variabile locale. Il che significa che il buffer è stato allocato nello stack. Ora, con gli ultimi 66 byte scritti, hanno appena sovrascritto 66 byte dello stack.
Se scrivi abbastanza dati oltre la fine di quel buffer, sovrascrivi l'altra variabile locale do_something_with_a()
(anche se non va bene se la usi in seguito), qualsiasi puntatore a frame stack che è stato salvato nello stack e poi indirizzo di ritorno della funzione. Ora sei davvero andato e hai rovinato tutto. Perché ora l'indirizzo di ritorno è qualcosa di completamente sbagliato. Quando viene raggiunta la fine di %code% , accadono cose brutte.
Ora possiamo aggiungere un ulteriore all'esempio sopra.
void do_something_with_a(char *a, char *new_a) {
int a_len = 0;
char new_array[16];
// Don't know what the length of the 'a' string is, but it's a string so
// lets use strlen()!
a_len = strlen(a);
//
// By the way, copying anything based on a length that's not what you
// initialized the array with is horrible horrible coding. But it's
// just an example.
//
// Gonna munge the 'a' string, so lets copy it first into new_array
strncpy(new_array, a, a_len);
// 'a_len' was on the stack, that we just blew away by writing 66 extra
// bytes to the 'new_array' buffer. So now the first 4 bytes after 16
// has now been written into a_len. This can still be interpreted as
// a signed int. So if you use the example memory, a_len is now 0xe0f33f5a
//
// ... did some more munging ...
//
// Now I want to return the new munged string in the *new_a variable
strncpy(new_a, new_array, a_len);
// Everything burns
}
Penso che i miei commenti spieghino praticamente tutto. Ma alla fine hai scritto una quantità enorme di dati in un array molto probabilmente pensando che stai scrivendo solo 16 byte. A seconda di come si manifesta questa vulnerabilità, ciò potrebbe portare allo sfruttamento tramite l'esecuzione di codice in modalità remota.
Questo è un esempio molto ingegnoso di scarsa codifica, ma puoi vedere come le cose possono aumentare rapidamente se non stai attento quando lavori con la memoria e copia i dati. Il più delle volte la vulnerabilità non è così ovvia. Con programmi di grandi dimensioni hai così tanto da fare che la vulnerabilità potrebbe non essere facile da individuare, e potrebbe essere innescata dal codice di chiamate a più funzioni.
Per ulteriori informazioni su come funzionano gli overflow del buffer .
E prima che qualcuno lo menzioni, ho ignorato l'endianess quando ho fatto riferimento alla memoria per motivi di semplicità
Ulteriori letture
Descrizione completa della vulnerabilità
Voce di enucleazione comune (CWE)
Presentazione delle stringhe di codifica protetta (download PDF automaticamente)
Università di Pittsburgh - Secure Coding C / C ++: Vulnerabilità delle stringhe (PDF)