In this article, we will be solving remaining challenges from Protostar VM by exploit-exercises.com.

Introduction

In this article, we will see how to find and conclude the offsets to EIP (Extended Instruction Pointer) by just looking at the disassembly of the program. We will also have a look at bypassing return address restrictions using techniques like ret2libc, ROP chains, Return to text and Return to register.

Downloads

VM used in this article can be downloaded from here.

Stack 4

In stack-4 challenge, we need to redirect the execution flow of the program and execute win function which is not supposed to execute ideally. Before proceeding with stack-4 challenge, let’s have a look at the disassembly of stack-3. As can be seen, in the comments section we have concluded that we need 64 bytes to overwrite the fp function pointer.

Let’s have a look at the code and disassembly of stack-4 program. As can be seen, the buffer size remains 64 bytes, so one can assume by passing the address of win function after 64 bytes I should be able to solve this challenge.

#include <stdlib.h>

#include <unistd.h>

#include <stdio.h>

#include <string.h>

void win()

{

printf(“code flow successfully changed\n”);

}

int main(int argc, char **argv)

{

char buffer[64];

gets(buffer);

}

Even after looking at the disassembly of the program, we can see that calculated offset is (0x50-0x10) = 0x40 = 64 bytes. So ideally our previous assumption should work here.

So, with our previous assumptions we modified our buffer, and as can be seen, we were not even able to crash the program.

So, what’s missing, well if we see carefully our input does start at $esp+0x10 i.e. 16 bytes from the $esp but here the $ebp is still 4 bytes away, and the return address is after ebp so return address is total 8 bytes away.

If we look at the stack, after gets function call, we can see that ret address is still 12 bytes away from 64 bytes of junk, those 12 bytes consist of 4 bytes for some address in win function which is now overwritten by 0x42424242, 4 bytes of padding and address of $ebp.

Also, to clarify the address 0xb7eadc76 is returning to some other stack frame, we examine the address, and in fact, it is pointing to the exit routine in libc.

As can be seen, the theory deduced by us in earlier steps is correct, and we were able to execute the win function. This happened because we have kind of overwritten the entry to exit routine with the address of win function.

Stack 5:

Stack5 introduces us the concept of shellcode. As can be seen, the code and disassembly are same irrespective of win function that we have seen in stack4. Here we need to execute our own shellcode.

#include <stdlib.h>

#include <unistd.h>

#include <stdio.h>

#include <string.h>

int main(int argc, char **argv)

{

char buffer[64];

gets(buffer);

}

By looking at the following disassembled code, we can confirm that offset to EIP remains same as of stack4.

We confirmed the offset as following and noted the address of ESP where our shellcode will be placed.

We further passed our payload to the program by replacing the return address with the address of our shellcode and were able to execute our shellcode.

Stack 6

This level introduces the concept of restriction on the return address. As can be seen, the code checks whether the return address starts from 0xbf000000 which is the range falling in stack address. As the challenge states, there can be multiple solutions to this problem. So, we will solve this challenge with multiple approaches.

#include <stdlib.h>

#include <unistd.h>

#include <stdio.h>

#include <string.h>

void getpath()

{

char buffer[64];

unsigned int ret;

printf(“input path please: “); fflush(stdout);

gets(buffer);

ret = __builtin_return_address(0);

if((ret & 0xbf000000) == 0xbf000000) {

printf(“bzzzt (%p)\n”, ret);

_exit(1);

}

printf(“got path %s\n”, buffer);

}

int main(int argc, char **argv)

{

getpath();

}

As the binary is straightforward like previous one, to demonstrate the exact offset to $eip we pass only 72 bytes as our input so that we can see what we need to overwrite. As can be seen, we need 8 bytes more to overwrite $ebp and next four to overwrite the return address to main function stack frame which will eventually become $eip.

We further check whether we can use any memory location from stack just to clarify that if the condition is kicking in or not. Well, it is.

Solution 1 using (Duplicate Payload):

So, after going through multiple memory locations, we found our payload is also placed at memory location starting with 0xb7 and therefore can be used as return address to jump to

As can be seen, we were able to execute our shellcode, by replacing the return address and to duplicate memory location and updating the start of payload with simple list files shellcode.

Our payload will be 34 bytes of shellcode + 46 bytes of junk + address referring to start of shellcode.

Solution 2 using (Return to text):

As we cannot use the return address starting with 0xbf we can simply use a ret instruction instead as the stack is executable this will put next memory address into EIP, and our shellcode will get executed which in this case is simply a trap instruction.

Our payload will be: 80 bytes of junk + ret + address of esp referring to our shellcode + shellcode

Solution 3 using (Return to libc)

We can also use ret2libc technique to bypass current ret address restriction as the libc base address starts from 0xb7.

We find out the base address of libc and offsets to system function and string /bin/sh as follows:

Our final exploit looks like following:

As can be seen, we were able to execute system command using the ret2libc technique.

Solution 4 using (ROP chain)

For constructing an ROP chain, we used following gadgets.

  • pop eax,pop ebx ,leave,ret
  • call eax, leave ret

Ethical Hacking Training – Resources (InfoSec)

Our final exploit looks like following, please go through the comments in the image to understand how selected gadgets work.

Note: The offset to stack addresses vary in gdb and terminal also you can see I am using hardcoded offsets which might not work in your case.

As can be seen, we were able to execute our shellcode using simple ROP chain.

Stack 7

Stack7 implements more restrictions on return address and now we cannot use address starting with 0xb eliminating the ret2libc technique too.

#include <stdlib.h>

#include <unistd.h>

#include <stdio.h>

#include <string.h>

char *getpath()

{

char buffer[64];

unsigned int ret;

printf(“input path please: “); fflush(stdout);

gets(buffer);

ret = __builtin_return_address(0);

if((ret & 0xb0000000) == 0xb0000000) {

printf(“bzzzt (%p)\n”, ret);

_exit(1);

}

printf(“got path %s\n”, buffer);

return strdup(buffer);

}

int main(int argc, char **argv)

{

getpath();

}

Return to Register:

The interesting thing about stack7 is it uses the strdup function, let’s have a look at the manual of strdup function. As we can see that this function takes one argument and returns a pointer to the duplicated string. We know the function return values in x86 arch is handled by eax register so we can directly jump to it and execute our shellcode.

Let’s test our theory using dummy payload. As can be seen, eax holds the pointer to our input.

Our final exploit looks like following

As can be seen, we were able to execute our shellcode using ROR technique. This challenge can also be solved using return to .text

References: