ret2win
64 bit
Let's run the executable to check what it does.
$ ./ret2win
ret2win by ROP Emporium
x86_64
For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer!
What could possibly go wrong?
You there, may I have your input please? And don't worry about null bytes, we're using read()!
> aaaaaa
Thank you!
ExitingIt takes user input and then exits.
We can use the checksec utility in order to identify the security properties of the binary executable.
$ checksec ret2win
[*] '/home/hacker/ropEmporium/ret2win/ret2win'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)There's two important properties we want to focus on here:
NX enabled: This means that the stack is not executable. Therefore we cannot use a shellcode injection.No PIE (0x400000): This means that the executable is not positionally independent and it is always loaded at address0x400000. So the code and memory regions will have the same address every time we run it.
Let's open the executable using gdb-pwndbg and look at the functions.
The pwnme and ret2win functions look interesting. We can use the disassemble command to see the instructions.
pwnme()
This function seems kind of useless as it isn't accessing the flag file.
ret2win()
If we look at the instruction at ret2win+19, we can see a system call. The instruction at ret2win+14 loads the argument for that same system call.
On examining the argument, we can see that it is in fact the flag file. Now we know that the ret2win function needs to be called in order to get the flag.
Let's disassemble main to check if the ret2win or pwnme function is being called.
main()
We can see that the pwnme function is being called but not the ret2win function. Therefore we will have to perform a buffer overflow in order to alter program flow and execute ret2win.
We first have to find the distance between the buffer and the return address.
Cyclic pattern
We can use a cyclic pattern to find this.
Let's run the executable again and provide this cyclic pattern as the input.
We can now look at the registers.
The rbp register points to 0x6161616161616165 which is the little endian eaaaaaaa in ASCII.
Let's find the offset of this value in our cyclic pattern.
So the offset is 32 bytes.
Let's see how this looks on the stack.
We can see that if we increment the rbp by 8, it will point to the saved return address.
Therefore the distance between the buffer and the saved return address is offset+8 which is equal to 40.
Exploit requirements
We now have all the information we need to create an exploit.
Exploit
Let's run the exploit.
32 bit
In order to create an exploit we need to know the following:
ret2win()
Let's disassemble ret1win.
So the address of ret2win is 0x0804862c in the 32-bit executable. One thing to note is that the arguments for a 32-bit function call are pushed on the stack whereas the arguments for a 64-bit function call are stored in registers.
You can check out live overflow's video if you to know more differences in 64-bit and 32-bit assembly.
Cyclic pattern
Let's create a cyclic pattern.
Let's provide this pattern as the input.
We can see that the ebp has the value 0x41304141 which is the little endian AA0A in ASCII.
Let's find the offset of this value in our cyclic pattern.
So the offset is 40 bytes.
Let's see how this looks on the stack.
We can see that if we increment the ebp by 4, it will point to the saved return address.
Therefore the distance between the buffer and the saved return address is offset+4 which is equal to 44.
Exploit requirements
We now have all the information we need to create an exploit.
Exploit
Let's run the exploit.
Last updated
Was this helpful?