Ret2esp / Ret2reg
Ret2esp
Poiché l'ESP (Stack Pointer) punta sempre alla cima dello stack, questa tecnica prevede la sostituzione dell'EIP (Instruction Pointer) con l'indirizzo di un'istruzione jmp esp
o call esp
. In questo modo, lo shellcode viene posizionato subito dopo l'EIP sovrascritto. Quando l'istruzione ret
viene eseguita, ESP punta all'indirizzo successivo, precisamente dove lo shellcode è memorizzato.
Se Address Space Layout Randomization (ASLR) non è abilitato in Windows o Linux, è possibile utilizzare le istruzioni jmp esp
o call esp
trovate nelle librerie condivise. Tuttavia, con ASLR attivo, potrebbe essere necessario cercare queste istruzioni all'interno del programma vulnerabile stesso (e potresti dover superare PIE).
Inoltre, essere in grado di posizionare lo shellcode dopo la corruzione dell'EIP, piuttosto che nel mezzo dello stack, garantisce che eventuali istruzioni push
o pop
eseguite durante il funzionamento della funzione non interferiscano con lo shellcode. Questa interferenza potrebbe verificarsi se lo shellcode fosse posizionato nel mezzo dello stack della funzione.
Spazio insufficiente
Se ti manca spazio per scrivere dopo aver sovrascritto RIP (forse solo pochi byte), scrivi uno shellcode iniziale jmp
come:
E scrivere lo shellcode all'inizio dello stack.
Esempio
Puoi trovare un esempio di questa tecnica in https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp con un exploit finale come:
Puoi vedere un altro esempio di questa tecnica in https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html. C'è un overflow di buffer senza NX abilitato, viene utilizzato un gadget per ridurre l'indirizzo di $esp
e poi un jmp esp;
per saltare al codice della shell:
Ret2reg
Allo stesso modo, se sappiamo che una funzione restituisce l'indirizzo in cui è memorizzato il codice della shell, possiamo sfruttare le istruzioni call eax
o jmp eax
(note come tecnica ret2eax), offrendo un altro metodo per eseguire il nostro codice della shell. Proprio come eax, qualsiasi altro registro contenente un indirizzo interessante potrebbe essere utilizzato (ret2reg).
Esempio
Puoi trovare alcuni esempi qui:
strcpy
memorizzerà ineax
l'indirizzo del buffer in cui è memorizzato il codice della shell eeax
non viene sovrascritto, quindi è possibile utilizzare unret2eax
.
ARM64
Ret2sp
In ARM64 non ci sono istruzioni che consentono di saltare al registro SP. Potrebbe essere possibile trovare un gadget che sposta sp in un registro e poi salta a quel registro, ma nella libc del mio kali non ho trovato alcun gadget del genere:
L'unico che ho scoperto cambierebbe il valore del registro in cui è stata copiata sp prima di saltarci (quindi diventerebbe inutile):
Ret2reg
Se un registro ha un indirizzo interessante, è possibile saltarci semplicemente trovando l'istruzione adeguata. Potresti utilizzare qualcosa del genere:
In ARM64, è x0
che memorizza il valore di ritorno di una funzione, quindi potrebbe essere che x0 memorizzi l'indirizzo di un buffer controllato dall'utente con uno shellcode da eseguire.
Codice di esempio:
Controllando il disassemblaggio della funzione è possibile vedere che l'indirizzo del buffer (vulnerabile a bof e controllato dall'utente) è memorizzato in x0
prima di tornare dal buffer overflow:
È anche possibile trovare il gadget br x0
nella funzione do_stuff
:
Utilizzeremo quel gadget per saltarci sopra perché il binario è compilato SENZA PIE. Utilizzando un pattern è possibile vedere che l'offset del buffer overflow è 80, quindi l'exploit sarebbe:
Se invece di fgets
fosse stato usato qualcosa come read
, sarebbe stato possibile aggirare anche il PIE sovrascrivendo solo gli ultimi 2 byte dell'indirizzo di ritorno per tornare all'istruzione br x0;
senza bisogno di conoscere l'indirizzo completo.
Con fgets
non funziona perché aggiunge un byte nullo (0x00) alla fine.
Protezioni
NX: Se lo stack non è eseguibile, questo non aiuterà poiché dobbiamo inserire il codice shell nello stack e saltare per eseguirlo.
Riferimenti
Last updated