Have you tried pwntools-ruby?

nc 54.65.72.116 31337
start-7a3715a6bd61783850f9c109dbc57571.zip

We are given two files. A server code written in ruby and an ELF binary which is statically linked.

Let’s look at the ruby script first.

It says ‘The binary “start” is listening at 127.0.0.1:31338.’, however we are not able to connect to that port because it listens on localhost only. The trick is we can execute random ruby code thanks to eval instruction. Since the server has pwntools-ruby installed and included it in the script, we can force the script to create connection to the port 31338. After that, we can exploit the server application to run a command like ls and print the result. Notice that we won’t be able to get an interactive shell, because the script closes STDIN after reading our input.

Before moving to the analysis of the binary file, I wanted to look at the server files by executing system commands.

Since I cannot use these functions, I decided to create a ruby script to do the job.

Let’s execute the script and look at the files.

I couldn’t find anything interesting. Even though I was sure that the flag was only available from the port 31338, I wanted to search all the files that contain the string ‘hitcon’ and created a ruby script for this task as well.

After executing the script, I couldn’t find anything again. So, it is time to exploit the binary start!

Let’s start with analyzing the protections.

The stack is not executable and there is a stack canary. Let’s disassemble the main to see what we have.

The program calls alarm(10) at the beginning, so we have only 10 seconds before our connection drops. We can disable the alarm by calling alarm(0), but it is not required for this challenge.

The program has a loop which reads up to 217 bytes from stdin and if the input is not “exit”, then it prints it back using puts. It will keep reading and printing until SIGALRM signal occurs or user sends “exit” as an input.

Let’s put a breakpoint on 0x00400b5c and look at the buffer before it gets filled to find the offsets of the stack cookie and the return address.

Wee see that our stack cookie is at buffer+24 and return address is at buffer+40. Now, we can start creating our exploit. However, we need to create a ropchain since the file is statically linked. There is no system function and I couldn’t find any hidden useful function. Let’s use ROPgadget to create a ropchain.

Wow, this one is really too long and our ropchain shouldn’t be larger than 177 bytes. For calling execve, the value of rax must be 59. So, I removed all add rax instructions and replaced them with a pop rax. Also, it uses “pop rsi; ret” and “pop rdx; ret”, but the binary has the gadget “pop rdx; pop rsi; ret” at 0x0000000000443799.

Here is the shorter version.

It is 168 bytes, so we can use it. Normally, I would also prepend the following to our ropchain in order to disable the alarm. However, after 177 bytes we exceed the limit of read and these lines will add 24 extra bytes which makes our ropchain 192 bytes long.

Either way we won’t get an interactive shell on the server, so let’s skip this code and don’t touch the alarm.

Let’s create our exploit to test it locally first. I will use python since I couldn’t find the process function in ruby-pwntools. We will first send 24 bytes of padding and a newline character to overwrite the null byte at the beginning of the stack cookie. Then, we will read the stack cookie. Next, we will send 40 bytes of padding including the stack cookie and our ropchain. Finally, we will send “exit” to trigger the return and get a shell.

Here is my python script.

Let’s test our script.

It works! Now, we need to write this exploit in ruby and send it as a string to the server to get it executed. Since, we can’t get an interactive shell. I will send just one command as an argument for our script. Also, notice that server.rb reads up to 1024 bytes. Therefore, I will make our string shorter by removing the comments. You can also remove the unnecessary zeroes from the addresses, merge some lines, remove tabs and spaces etc.

It seems like the implementaton of recv has a bug in pwntools-ruby. It does not read the newline character at the end. In order to handle this issue, we need to add an extra recvline. I also noticed that sometimes it says “Stack smashing detected!”, so I thought what could cause this problem and noticed that we rely on recv again to retrieve the stack cookie. Since recv has some issues with newlines, we can’t get the stack cookie properly if the cookie includes any newline (0x0A). To solve this issue I replaced recv with recvn(32) to make sure that I got 32 bytes whose last 7 bytes are our stack cookie.

Here is the final exploit in ruby.

Let’s execute our script to finally get our flag.

Here is our flag hitcon{thanks_for_using_pwntools-ruby:D}.