In this article, we will be solving all networking challenges and one remote buffer overflow challenge of Protostar.
These levels introduce us to the fundamental concept of sending and receiving data over a network in a different format, and the hurdles of debugging and developing an exploit for remote stack overflows. We will have a look at how we can debug and understand what inputs the program expects from us.
The VM used in this article can be downloaded from here
The following assembly code is obtained after disassembling the Net0 binary. As we can see, the application executes run function from the main function. The run function generates a random number and uses printf function call to print it on the screen. It further checks whether our input matches to the random() number generated by comparing our raw input and random number generated previously.
Our logic for solving this challenge will be composed of following steps:
- Connect to server
- Recv() some data
- Parse the data and take out the random number ask by the application
- Pack the random number in little-endian format and send it back.
Using the logic listed above, we came up with the following piece of code and successfully passed this challenge.
By looking at the disassembled code of binary Net1, we can understand following things:
- The program generates a random number and stores the integer as a string at ebp-0x24.
- It then writes the content of number generated to stdout.
- It then takes input from user removes CRLF characters and compare the input with the contents of ebp-0x24 variable saved earlier.
As in this case, the data is already packed in 32-bit integers. We will be using similar logic from the NET0 level and after receiving the data we will be unpacking it to 32-bit integer and send the unpacked value back as it is.
Finally, we came up with following piece of code that will solve this challenge.
By looking at the disassembled code of binary Net2, we can understand following things:
- The application generates four random numbers and sends in four iterations using write ().
- It also keeps on adding them and stores the overall result at ebp-0xc.
- As a result might overflow its limit, it will wrap, so we need to take care of that in our code too.
- Finally, it checks the input provided by the user matches the added numbers, so we need to pack our input to an unsigned int to take care of that.
We came up with the following piece of code to solve this challenge using the following algorithm:
- Connect to the server.
- recv () four bytes of data from the server iteratively four times and add them.
- DO an AND operation with the final sum, so the result stays in 32 unsigned int’s range.
- Pack the result and send it back to the server.
This level takes us back to simple stack based buffer overflows with little restrictions added here and there. Let’s have a look at the disassembled code for this level. By looking at the code we can quickly figure out the vulnerable function gets() is used but the problem is our input gets passed through the for loop which iterates each of our provided bytes thus it will corrupt any non-alpha uppercase shellcode.
So, let’s start with fuzzing the application and figuring out the offset to EIP. First, we use Python’s pwntools library for generating a cyclic pattern and end up with following snippet of code.
An observant user must have noticed that application is using forking technique to serve connections thus debugging using process id will not work for runtime analysis. Therefore, we need to debug child process. We placed a breakpoint after strdup (0x0804982a) and started analyzing the state of the process.
As we will need to debug the same process multiple times so it will be a bit painful to set gdb commands every time, therefore we will be using the .gdbinit file for the sake of automation.
After editing the .gdbinit file and sending our fuzz string we got an $eip overwrite at 0x46414149, our input is placed on the stack and $eax points to some part of our shellcode mainly from the start because of strdup() function which returns in $eax. We further used cyclic_find function from pwntools to get offset to $eip at 532.
Ethical Hacking Training – Resources (InfoSec)
Now there are multiple options for writing exploit for this program:
- Return to register: call eax (required Alpha upper shellcode)
- Jump to stack: At the time of the crash, no pointers to our shellcode were present on the stack we need to rely on the hardcoded address (Not a reliable way). Also, we do not need alphanumeric shellcode in this case as gets will copying our input into buffer after it meets a null bytes (\x00).
- ROP Chain via ret2libc: As there is no ASLR enabled on the system we can rely on the PLT address from the executable itself construct a simple ROP chain. (Reliable unless ASLR is enabled even in that case we need to modify our shellcode to make it work again slightly)
Our ROP chain will do the following:
- Read the contents of system command in .dynamic section.
- Pass the pointer of .dynamic to execve.
As we see in the following screenshot, we were able to obtain remote shell by exploiting final0 binary.