2010-07-20 10 views
15

Ho letto molti articoli su funzioni non sicure come strcpy, memcpy, ecc. Che possono causare problemi di sicurezza durante l'elaborazione di dati esterni, come il contenuto di un file o dati provenienti da socket. Questo può sembrare stupido, ma ho scritto un programma vulnerabile ma non sono riuscito a "hackerarlo".Esempio di overflow del buffer che porta a una perdita di sicurezza

Capisco il problema dell'overflow del buffer. Prendete questo codice di esempio:

int main() { 
    char buffer[1]; 
    int var = 0; 

    scan("%s", &buffer); 
    printf("var = 0x%x\n", var); 
    return 0; 
} 

Quando eseguo il programma e digitare "ABCDE", i risultati del programma 0x65646362 che è "edcb" in esadecimale + little-endian. Tuttavia ho letto che è possibile modificare il valore eip che è stato inserito nello stack per fare in modo che il programma esegua del codice indesiderato (ad es. Prima di una chiamata alla funzione system()).

Tuttavia assemblaggio della funzione inizia così:

push %ebp 
mov %ebp, %esp 
and $0xfffffff0, %esp 
sub $0x20, %esp 

Poiché il valore del% è esp casuale all'inizio della funzione e per questo "e", non sembra esserci alcun modo affidabile per scrivere un valore preciso nel valore di eip spinto.

Inoltre, ho letto che era possibile eseguire il codice che hai scritto nel buffer (qui il buffer è lungo solo 1 byte, ma in realtà sarebbe abbastanza grande per memorizzare del codice) ma quale valore daresti eip per farlo (considerando la posizione del buffer è casuale)?

Quindi perché gli sviluppatori sono così preoccupati per problemi di sicurezza (tranne che il programma potrebbe bloccarsi)? Avete un esempio di un programma vulnerabile e come "hackerarlo" per eseguire codice indesiderato? Ho provato questo su Linux, Windows è meno sicuro?

risposta

15

Leggi l'eccellente articolo di Aleph One: Smashing the Stack for Fun and Profit.

+0

Correggimi se ho torto ma, per quanto seminale, non mi ricordo di questo articolo che riguarda i meccanismi di protezione dello stack. – torak

+0

@torak: No, non lo è. Non credo che questo sia il problema dell'OP, e la domanda sembra più basilare che affrontare le salvaguardie dell'OS/hardware. Questo articolo è un ottimo punto di partenza. –

+0

@Moron: Beh, dato che l'OP ha specificamente menzionato che l'istruzione 'e $ 0xfffffff0,% esp' rende l'offset alla variabile EIP, sembrerebbe che afferrino il meccanismo di overflow di base. – torak

4

Beh, per prima cosa, non sottovalutare i rischi associati alla possibilità di inserire un valore in EIP in modo inaffidabile. Se un exploit funziona una volta su 16, e il servizio che sta attaccando si riavvia automaticamente, come molte applicazioni web, quindi un utente malintenzionato che fallisce nel tentativo di ottenere l'accesso può sempre provare, riprovare.

Anche in molti casi il valore di ESP è meno casuale di quanto si pensi. Per i principianti su un sistema a 32 bit è quasi sempre un multiplo di quattro. Ciò significa che l'imbottitura aggiuntiva offerta dall'istruzione and $0xfffffff0, %esp sarà 0, 4, 8 o 12 byte. Ciò significa che è possibile solo ripetere il valore che deve essere scritto nel ritorno EIP quattro volte per coprire tutte le possibili correzioni all'indirizzo EIP di ritorno.

In realtà esistono meccanismi molto più aggressivi stack protection/buffer overflow detection. Tuttavia, ci sono modi e mezzi attorno anche a questi.

Inoltre, per un esempio di dove questo tipo di cose può essere pericoloso, considerare se il valore di var era importante per la logica come nell'esempio di esempio seguente.

int main() { 
    char buffer[1]; 
    int var = 0; 

    var = SecurityCheck(); 

    scan("%s", &buffer); 
    if (var != 0) 
    GrantAccess(); 
    else 
    DenyAccess() 
} 
+1

I In breve, puoi mettere un sacco di NOP prima del tuo shellcode, quindi indovinare dove è "l'inizio" del tuo shellcode diventa più facile – ninjalj

+0

Strettamente correlato a questo esempio è la tecnica di corrompere una struttura come ' struct user_account {char name [16]; int group; int permissions;}; 'fornendo un' name' che è troppo lungo per sovrascrivere i membri 'group' e' permissions'. La chiave è in grado di prevedere cosa si trova in memoria nei byte che seguono una matrice – bta

1

Un classico esempio di un vero e proprio exploit basati su buffer overrun è il Morris Worm del 1988.

+0

+1: stavo pensando a questo stesso esatto esempio! –

+1

In realtà, il worm Morris ha sfruttato diverse vulnerabilità, una delle quali era una buffer overflow in fingerd. – ninjalj

+0

@ninjalj: Sì, sembra accurato. –

4

Inoltre non c'è bisogno di sovrascrivere EIP con un puntatore a qualcosa nella stringa. Ad esempio, è possibile sovrascriverlo con un puntatore su system() e sovrascrivere la parola successiva con un puntatore su /bin/sh in una posizione fissa nell'immagine del programma.

Modifica: Si noti che system utilizza il PATH (in realtà esegue il comando tramite una shell), quindi "sh" sarebbe altrettanto valido; quindi, qualsiasi parola inglese che termina con "sh" alla fine di una stringa fornisce l'argomento di cui hai bisogno.

0

Ecco una versione di Windows e tutorial:

http://www.codeproject.com/KB/winsdk/CodeInject.aspx

Il caso generale mi ha sempre avvertito era:

printf(string); 

Poiché l'utente può fornire un "%n" in là, che consente per inserire tutto ciò che vuoi in memoria. Tutto quello che devi fare è trovare l'offset di memoria per una chiamata di sistema, passare alcuni caratteri di "%n" e junk e quindi inserire l'indirizzo di memoria nello stack in cui il vettore di ritorno sarebbe normalmente. Voila: inserisci il codice che ti piace.

+2

Si chiama vulnerabilità di stringa di formato, non di buffer overflow :) – ninjalj

+0

@ninjali: meh, sono cugini kissin '. questo strappa il buffer dello stack. :-) – eruciform

1

Come accennato in altre risposte, l'affidabilità assoluta non è sempre essenziale affinché l'attacco abbia successo. Le applicazioni che si riavviano automaticamente sono un esempio. I buffer overflow utilizzabili localmente sui programmi suid sarebbero un altro. E c'è la tecnica della slitta NOP per aumentare le possibilità di successo dello sfruttamento, mettere un sacco di NOP prima del codice shell in modo da avere una migliore possibilità di indovinare correttamente lo "start" dello shellcode.

Esistono molte altre tecniche per aumentare l'affidabilità degli attacchi. Su Windows, nel passato, molti exploit hanno sovrascritto l'indirizzo di ritorno con l'indirizzo di un "jmp% esp" che si trova da qualche parte nel programma (trampolino).

"Programmazione insicura con l'esempio" ha avuto un bel trucco per Linux. Pulisci il tuo ambiente e inserisci il tuo shellcode in una variabile di ambiente. Indietro nel giorno, questo porterebbe a un indirizzo prevedibile vicino alla cima della pila.

E ci sono anche varianti come la programmazione return-in-libc e return-oriented.

C'era persino un articolo su Phrack su come sfruttare gli overflow dello stack di 1 byte (il che significa che il buffer era sovraccarico di un solo byte) (gli straripamenti degli heap da 1 byte sono anche sfruttabili nella stragrande maggioranza dei casi, a eccezione protezioni).

In sintesi, non è che gli sviluppatori sono paranoico, ci sono un sacco di modi per sfruttare anche i casi più strani, e ricordate:

  • Un programma è di buona qualità quando si fa quello che si suppone fare.
  • Un programma è sicuro quando fa quello che dovrebbe fare, e nient'altro.
Problemi correlati