Se lo stack cresce verso il basso, come può un overflow del buffer sovrascrivere il contenuto sopra la variabile?

7

Mi rendo conto di come funziona un overflow del buffer, ma ho un problema a capire la direzione in cui è diretto l'overflow. Pertanto, se lo stack aumenta verso il basso , significa che l'indirizzo di ritorno è sopra lo spazio riservato della variabile. Quando la variabile è ora in overflow, non dovrebbe sovrascrivere la memoria sotto anziché sopra?

tl; dr: se lo stack cresce verso il basso, come può un overflow del buffer sovrascrivere il contenuto sopra la variabile?

    
posta AdHominem 03.09.2016 - 10:45
fonte

4 risposte

6

Se lo stack cresce verso il basso, le funzioni chiamate in seguito ottengono frame stack a indirizzi di memoria inferiori. Inoltre, l'indirizzo di ritorno viene inserito nello stack prima che lo spazio per le variabili locali sia riservato, quindi l'indirizzo di ritorno ottiene un indirizzo superiore rispetto alle variabili locali. Ma gli array e i buffer sono ancora indicizzati verso l'alto in memoria, quindi scrivere oltre la fine dell'array colpirà l'indirizzo di ritorno immediatamente dopo lo stack.

Esempio, con arte ASCII obbligatoria:

Considera la funzione banale che prende input da una fonte non attendibile e la copia su un buffer locale:

void foo(char *s)
{
    char buf[8];
    strcpy(buf, s);
    return;
}

Lo stack assomiglia un po 'a questo:

   <---- stack grows to the left
    memory addresses increase to the right -->
  0x8000                        0x8010
  +--------+----------+---------++------------
  + buf[8] | ret addr | char *s ||   ....... 
  +--------+----------+---------++--------------
   <--------- foo() ----------->  <---- caller --

Lo stack viene riempito da destra a sinistra, iniziando dagli argomenti della funzione, quindi dall'indirizzo di ritorno, quindi dai locals della funzione. È facile vedere che un semplice overflow da buf verso gli indirizzi crescenti colpirà l'indirizzo di ritorno in modo piacevole.

Quindi, cosa succede se lo stack è invertito e cresce verso l'alto? Quindi il sovraccarico di un buffer verrà eseguito allo stesso modo in cui lo stack cresce, verso la parte vuota dello stack.

Sembra buono, ma non aiuta se foo() chiama un'altra funzione per fare la copia. Il che non è insolito, l'ho appena fatto con strcpy . Ora la pila assomiglia a questo:

    stack grows to the right -->
    memory addresses increase to the right -->
            0x8000                          0x8010
------------++---------+----------+---------++-----------+-------------+
  ....      || char *s | ret addr | buf[8]  || ret addr  | locals  ... |
------------++---------+----------+---------++-----------+-------------+
 caller --->  <-------- foo() ------------->  <---- strcpy() ---------->

Ora, sovrascrivendo (a destra) il buffer nel frame dello stack di foo() sovrascriverà piacevolmente l'indirizzo di ritorno di strcpy() , non foo() . Non importa, comunque, saltiamo comunque in una posizione imposta dai dati traboccanti e controllati dagli aggressori.

    
risposta data 03.09.2016 - 18:16
fonte
3

C'è una differenza tra buffer overflow e stack overflow. I buffer allocati non possono utilizzare lo stack, ma l'heap. Ciò dipende dal modo in cui sono allocati e dal fatto che il compilatore sarebbe meglio / più veloce / ecc.

Un overflow dello stack sovrascrive la memoria in basso, che potrebbe essere stata assegnata a un'altra chiamata (precedente) o alla fine all'heap. Il mucchio cresce verso l'alto e ad un certo punto possono scontrarsi. Questo è un ottimo momento in cui il sistema operativo genera un errore di segmentazione, interrompe il programma o invia segnali. Il problema con lo stack overflow è che possono entrare in contatto con altri thread dello stesso processo. Ora un'immagine di un thread che ha un buffer grande per send / recv di rete, il contenuto di un altro stack di thread potrebbe sovrascrivere questa area di memoria causando perdite di memoria sulla rete.

Gli overflow del buffer sono più comuni e vanno oltre i limiti della propria memoria. Finché la memoria a cui si accede è ancora all'interno dell'area dell'heap, non c'è alcun problema (dal punto di vista del sistema operativo). Diventa più pericoloso quando l'heap è troppo piccolo e devono essere allocati dati aggiuntivi. A questo punto non si può dire cosa succederebbe quando si raggiungono i confini. Quando si accede a un byte oltre il bordo dell'heap, il sistema operativo dovrebbe uccidere il processo con un SIGSEGV.

    
risposta data 03.09.2016 - 11:43
fonte
3

Lo stack cresce solo verso il basso quando su di esso viene assegnato qualcosa. D'altra parte, leggere la fine di un array significa leggere in alto nella memoria.

Diciamo che il puntatore dello stack dice 0x1002. A questo indirizzo è un puntatore di ritorno che ti interessa. Se si assegna, ad esempio, un array a 2 byte, lo stack aumenta verso il basso: il puntatore dello stack viene modificato su 0x1000 e l'indirizzo dell'array viene impostato su quel nuovo valore. Scrivere il primo byte nell'array utilizza l'indirizzo 0x1000 e scrivere il secondo utilizza 0x1001. Scrivendo alla fine usa 0x1002 e sovrascrive la cosa che conta.

Puoi vederlo nelle immagini che hai postato, poiché la casella grigia che rappresenta il buffer B cresce verso l'alto poiché è scritta oltre i suoi limiti.

    
risposta data 03.09.2016 - 14:54
fonte
2

Uno stack cresce verso il basso tramite l'istruzione push, ma scrive e legge verso l'alto. per esempio, se il tuo stack occupa un indirizzo da 10 a 5 che è una lunghezza di 6 indirizzi utilizzabili, quando hai un'istruzione push, lo stack andrà giù e aggiungerà una memoria extra che comporterà lo stack che occupa l'indirizzo 10 - 4 che è 7 indirizzi utilizzabili. ma quando stai scrivendo nello stack e il tuo puntatore di istruzioni è a 4, scriverà da 4 in su a 10 e quando passerai l'indirizzo 10, allora avrai un buffer overflow

    
risposta data 08.02.2018 - 00:56
fonte

Leggi altre domande sui tag