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.