In this article we will address all network challenges and one Exploiting Protostar buffer overflow challenge.
Introduction [Exploiting Protostar]
These levels introduce us to the basic concept of sending and receiving data over a network in a different format, and the hurdles of debugging and developing a remote stack overflow exploit. We will look at how we can debug and understand what inputs the program expects from us.
The following build code is obtained after unpacking the Net0 binary. As we can see, the application runs the run function from the main function. The run function generates a random number and uses a call to the printf function to print it to the screen. Next, it checks if our input matches the random() number generated by comparing our raw input to the random number generated earlier.
Our logic for solving this challenge will consist of the following steps:
- Connect to the server
- Recv() some data
- Analyze the data and extract the random number that the application asks for
- Wrap the random number in little-endian format and send it back.
Using the above logic, we came up with the following code and successfully completed this challenge.
Related Article:IPL Bootkits :Rovnix and Carberp-by Blackhat Pakistan 2023
Looking at the exploded code of the Net1 binary, we can understand the following things:
- The program generates a random number and stores the integer as a string at ebp-0x24.
- It then writes the contents of the generated number to stdout.
- It then takes the input from the user, removes the CRLF characters, and compares the input with the contents of the ebp-0x24 variable stored earlier.
- As in this case, the data is already packed into 32-bit integers. We will use similar logic from the NET0 level and after receiving the data we will unpack it to a 32-bit integer and send the unpacked value back as is.
We finally came up with the following code to solve this challenge.
Looking at the exploded code of the Net2 binary, we can understand the following things:
- The application generates four random numbers and sends them in four iterations using write().
- It also keeps adding them and saves the total to ebp-0xc.
- As a result, it might exceed its limit, it will break, so we need to take care of that in our code as well.
- Finally, it checks that the input provided by the user matches the numbers added, so we need to wrap our input in an unsigned int to take care of that.
We came up with the following code to solve this problem using the following algorithm:
- Connect to the server.
- recv() four bytes of data from the server iteratively four times and add them.
- AND the final sum so that the result remains in the range of 32 unsigned int.
- Package the result and send it back to the server.
This level takes us back to a simple stack-based buffer overflow with little constraints added here and there. Let’s take a look at the exploded code for this level. Looking at the code, we can quickly see that a vulnerable gets() function is used, but the problem is that our input goes through a for loop that iterates over each of our supplied bytes, so it will corrupt any non-alpha uppercase shellcode.
So let’s start by fuzzing the application and determining the offset to the EIP. First, we’ll use Python’s pwntools library to generate a loop pattern and end up with the following code snippet.
An observant user must have noticed that the application uses forking to handle connections, so debugging with the process id will not work for runtime analysis. Therefore, we need to debug the child process. We placed a breakpoint (0x0804982a) behind strdup and started analyzing the state of the process.
Since we will need to debug the same process multiple times, it will be a bit of a pain to set up the gdb commands each time, so we will use a .gdbinit file for automation.
After editing the .gdbinit file and sending our fuzz string we got $eip overridden at 0x46414149, our input is put on the stack and $eax points to some part of our shellcode mainly from the beginning because of the strdup() function that returns in $eax. Next, we used the cyclic_find function from pwntools to get the offset to $eip at 532.
There are now several options for writing an exploit for this program:
- Return to register: call eax (requires alpha upper shellcode)
- Go to stack: At the time of the crash, no pointers to our shellcode were present on the stack, which we have to rely on a hardcoded address (not a reliable way). We also don’t need the alphanumeric shellcode in this case because it copies our input into the buffer after it encounters zero bytes (x00).
- ROP string via ret2libc: Since there is no ASLR enabled on the system, we can rely on the PLT address from the executable itself to create a simple ROP string. (Reliable if ASLR is not enabled, even then we need to modify our shellcode to make it work slightly again)
Our ROP chain will do the following:
- Read the contents of the system command in the .dynamic section.
- Pass the .dynamic pointer to execve.
As we can see in the following screenshot, we were able to get a remote shell using the final0 binary.