Il codice è già codice macchina 'compilato'. Fai qualcosa di simile in main()
:
int *ret;
ret = (int *)&ret + 2;
*ret = (int) buf;
Assicurati di disabilitare lo stack non eseguibile con -z execstack
flag durante la compilazione.
Modifica
Annotare il codice -
int *ret;
Stiamo dichiarando un puntatore int chiamato ret
come variabile locale in main()
. Essendo la prima e unica variabile, si trova immediatamente sotto il vecchio puntatore del frame nella pila.
ret = (int *)&ret + 2;
Successivamente, aggiungiamo 2 all'indirizzo del puntatore ret
e lo memorizziamo in ret
. Se si guarda la figura, l'indirizzo di ritorno si trova esattamente a 2 parole sopra ret
. Quindi stiamo praticamente memorizzando l'indirizzo dell'indirizzo di ritorno in ret
.
*ret = (int) buf;
Infine, stiamo dereferenziando ret
per sostituire l'indirizzo di ritorno esistente con l'indirizzo di buf
(il nostro shellcode - presumibilmente dichiarato globalmente).
main()
tornerà quindi a buf
invece di dove avrebbe dovuto. I byte in buf
saranno interpretati come codice e saranno eseguiti felicemente. Questo è ciò che normalmente accade in un exploit di overflow del buffer di vaniglia. Il flusso di controllo viene dirottato causando un overflow del buffer e facendolo sovrascrivere l'indirizzo di ritorno con qualcosa che l'hacker ha il controllo su (solitamente shellcode iniettato).
Si noti che i sistemi operativi moderni solitamente memorizzano canali di stack aggiuntivi per rilevare gli overflow. Pertanto l'offset "2" potrebbe non essere vero. Per semplicità, usa il flag -fno-stack-protector
mentre compili in gcc per disabilitare i canarini dello stack.