[Exploit-exercises] Fusion level02
This exercise was interesting because I used GDB without PEDA and we all know how this tool can rapidly be an essential when debugging. Hence, no searchmem command with which I was really used to.
The source code of the exercise can be found at exploit-exercises.com.
The “About” section of the challenge is:
This level deals with some basic obfuscation / math stuff
This level introduces non-executable memory and return into libc / .text / return orientated programming (ROP).
There are multiple solutions to this exercise that you can find just by googling its name. I used a return into libc approach.
Preparing GDB
Because it’s not so simple for beginners, let’s see how I configured GDB on the fusion virtual machine. First we want to log in as root because otherwise we can’t attach GDB to the level02’s process and then we use pgrep to retrieve the PID of the process.
At this point, we should say to GDB that we want to disassemble the code in the Intel style (I think it’s much easier to read). Then, we want to follow the child that the process will create when called. I’m not 100% sure but the program seems to listen to a specific port and fork to execute the source code we have. And we attach GDB to the level02 process.
Now GDB is ready.
Static analysis
The program starts with the encrypt_file function which will enter in a while loop that reads several times from the standard input and also writes to the standard output. It uses functions called nread and nwrite which are the obfuscation part of the exercise I guess. But after a quick look at these, it doesn’t seem to make any difference.
In the middle of the loop, cipher is called with ‘buffer’ and ‘sz’ as arguments. This function basically xor the buffer with a key that is taken from /dev/urandom. But this key is generated at the first call of the function and keeps its value for eventual next call because it’s a static variable.
Here is the description of the loop:
- The first nread takes an unsigned char (1 byte) and then comes a switch case wether it’s ‘E’ or ‘Q’.
- If it’s ‘E’, then it reads 4 bytes (size_t) and places it in ‘sz’. If it’s ‘Q’, the function returns.
- And here is our buffer overflow, it reads ‘sz’ bytes and stores it into ‘buffer’.
- The buffer is xored and if this is the first time cipher is called; the key is created.
- The first nwrite writes ‘sz’ (4 bytes) to stdout.
- The second one writes the xored buffer.
- Starts again
If we read more than the size of the ‘buffer’ (131072 bytes), we can override EIP. The assembly code just before the 3rd nread call is:
Our buffer is at $ebp-0x2000c.
Controlling EIP
Let’s try to send 0x2000c + 0x8 bytes (0x20014) as buffer, and see if it overrides EIP.
‘CCCC’ should be the new EIP (‘BBBB’ overrides EBP). The ‘Q’ allows us to quit the function without calling the exit function and then executing our new EIP.
Result:
I guess this is ‘CCCC’ but xored by the cipher function.
Now, as we saw that the key with which the buffer is xored is generated the first time cipher is called, we can presumably send the same size and the xored buffer in the second loop iteration because k xor (buffer xor k) = buffer (with k=key).
I used pwntools to write the exploit (awesome tool).
recvn(n) waits for exactly n bytes on the socket. Let’s run the script:
We now fully control EIP.
Return into libc
It was the most funniest part, because this is wehre I learned things. I choosed a return into libc approach because it seemed to be the most simple way. Return into libc is a classical attack and if you want to learn how it works this exploitdb paper explains it.
The classical way to exploit it is to execute the system function with ‘/bin/sh’ as an argument. EIP should be the address of system then a dummy return address (or exit function if you want to do it properly) and finally the address of ‘/bin/sh’. My challenge was, how to find this function address and this string without using GDB ?
First let see where is the libc:
Now, we can search for the address of system:
When searching for __libc_system on google we find this result and we can see that this function is called with an argument (line) which is then passed to another function (do_system) and again to __execve. The address of __libc_system is 0x0003cb20.
Then we have to find a string like ‘/bin/sh’. We can use the ROPgadget tool provided in the fusion VM but I wanted to find another way to search for a string and its address, and apparently we can use the string command:
The next step is to find where the libc is mapped for this binary, we can find it using /proc/PID/maps:
We clearly see that the libc is mapped from 0xb770b000 to 0xb7884000 and that its executable part (r-xp) start at 0xb770b000. When calling the __libc_system function and the ‘/bin/sh’ string, we’ll have to add the offset of the libc to their address.
Our exploit is now:
Now we have a shell!