Quelli che seguono sono appunti per memorizzare i concetti appresi, l’articolo non ha la pretesa di essere un tutorial.
Per lo studio è stato utilizzato vulnserver: https://github.com/stephenbradshaw/vulnserver
Riferimenti: https://www.corelan.be/index.php/2009/07/25/writing-buffer-overflow-exploits-a-quick-and-basic-tutorial-part-3-seh/
Online assembler: https://defuse.ca/online-x86-assembler.htm
OS: Windows XP SP2
Architettura: x86
Structured Exception Handler
SEH è il gestore delle eccezioni di Windows. Se lo sviluppatore non ha implementato delle eccezioni all’interno del software, nel momento in cui avviene un evento imprevisto nel flusso del programma, Windows prende in carico l’eccezione e cerca di gestirla come segue:
SEH è costituito da due parti fondamentali:
- nSEH – puntatore al SEH successivo
- SEH – SEH corrente
Se il SEH corrente non è in grado di gestire l’eccezione viene richiamato il gestore dell’eccezione successivo (nSEH) e così via (SEH chain).
E’ possibile sfruttare questa funzionalità di Windows durante un buffer overflow per eseguire del codice arbitrario sulla macchina.
Controllando SEH è possibile reindirizzare l’esecuzione su nSEH che a sua volta permette di reindirizzare l’esecuzione del programma avanti o indietro con un JMP SHORT dal momento che si hanno a disposizione solo 4 bytes (dimensione dell’indirizzo nSEH).
JMP SHORT: opcode EB XX dove XX è il numero di bytes che va da:
- 0x00 to 0x7f ==> jump avanti
- 0x80 – 0xff ==> jump indietro (0xff = -1, 0x80 = -128)
Esempio: 0xEB06 salta in avanti di 6 bytes.
Per completare la dimensione di 4 bytes del registro nSEH si possono usare i NOP: 0x909006EB (little endian)
Punto chiave
nSEH si trova sempre in ESP+8, quindi decrementando lo stack di 8 bytes ci ritroveremo ad eseguire nSEH. Si fa con l’istruzione POP POP RET. Questo accade perché l’istruzione RET salva in EIP il valore a cui punta ESP. EIP è il puntatore all’istruzione seguente che eseguirà il programma.
Dopo un buffer overflow, per sfruttare SEH gli step sono i seguenti:
- Calcolare offset SEH.
- Inserire in SEH opcode di POP POP RET per decrementare lo stack di 8 bytes e fare in modo che ESP punti a nSEH.
- Inserire in nSEH opcode di JMP SHORT per reindirizzare l’esecuzione dove si ha a disposizione un po’ più spazio: spazio per uno shellcode o per un mini shellcode che reindirizzi l’esecuzione alla destinazione finale dello shellcode.
Se il JMP SHORT ci basta per reindirizzare il flusso del programma in un punto della memoria con uno spazio sufficiente a contenere il nostro shellcode si va avanti, altrimenti occorre effettuare un altro salto in un punto della memoria con più spazio. Si può fare come segue:
PUSH EBP # 1
POP EAX # 2
ADD EAX, bytes # 3
JMP EAX # 4
- Pusha nello stack l’indirizzo corrente di ESP. Serve come riferimento per spostarsi all’interno della memoria.
- Salva in EAX l’indirizzo di ESP pushato nello stack e lo rimuove dallo stack.
- Aggiunge ad EAX* un numero di bytes in funzione di dove si vuole posizionare lo shellcode.
- Rimanda l’esecuzione dove è stato posizionato lo shellcode.
Nota sui registri: *EAX è il registro che comprende 32-bit. AX è il registro che rappresenta i 16-bit più bassi.
Alla pagina seguente l’esempio pratico.