2

General Background: I have written an echo server trying to implement an example of BoF in C that utilizes a strcpy() function call like such:

// .... including the corresponding libraries depending on host environment

#include <stdio.h>                                              // crossed platform headers
#include <string.h>                                             // string header, for strcpy
#include <stdlib.h>                                             // stardard lib, for exit()


void echo(void);                                 // infinite loop echo serving client
char* vuln_func(char* recieved);                                // BoF vulnerable function that uses strcpy from string.h


int main(void)
{
  ... setting up echo server
  echo();

  return 0;
}

void echo(void)
{
  char *send_str;                                                 // declaring the pointer to the string recieved from vulnFunc
  char recv_str[1024];                                            // variable declaration to store input string from client

  // infinite loop to echo serve clients
  while(1)

  {
    ... recieving string from client and storing it starting at location recv_str
    // calling vuln_func and inputting the received string from client

    send_str = vuln_func(recv_str);                       

    ...echoing the send_str back to the client

  }


}

char* vuln_func( char* recieved)
{
  char* sending_str = (char*)calloc(100, sizeof(char));                               

   char buf[100];                                                                 

  int i;                                                                          // declaring counter for loop
  strcpy(buf, recieved);
  for (i = 0; i < strlen(buf); i++)                                              // for loop to populate return string with the send
  {
    sending_str[i] = buf[i];
  }

  return sending_str;                                                             
}

Problem: So once we feed the server a big enough input string, the server will generate an Access Violation with offset 41414141 (which is What we wanted); however once we disassembled the exe in a disassembler/debugger (I used immunity Debugger)at the point of the violation neither the ESP nor the EIP was overwritten with the string of As (for instance). While the EBP register was overwritten with the overflown big string of As.

Solution: So I did some googling around and saw similar example codes of the same kind (Echo Server with a BoF vuln) and tried different things with the vuln_func like such:

void vuln_func( char* recieved)
{

   char buf[100];                                                                 

  strcpy(buf, recieved);

}

Or:

int vuln_func( char* recieved)
{

  char buf[100];                                                                 

  strcpy(buf, recieved);

  return 1;

}

and the echo function will look like such:

void echo(void)
{

  char recv_str[1024];                                            // variable declaration to store input string from client

  // infinite loop to echo serve clients
  while(1)

  {
    ... recieving string from client and storing it starting at location recv_str
    // calling vuln_func with string received from client

    vuln_func(recv_str);                       

    ...echoing the recv_str back to the client

  }


}

so have the echo function echo back the recv_str instead of the send_str which imho defeats the entire purpose of the vuln_func call, but for the purpose of demonstrating this BoF Vulnerability, we will leave it in there. After checking with Immunity Debugger, both the ESP and the EIP registers are successfully overwritten with the string ("A*128"), problem fixed!! Great

Question: Why did the problem behave that way?

  1. How come when returning a pointer(address) to a string to the calling function it prevents the EIP to be overwritten, while returning void, or int will cause the EIP register to be overwritten as expected
  2. How does the EIP register store its value in the two scanrios
    mentioned above (with one vuln_func returning char* and other vuln_func
    returning void )?
  3. Does the problem behaving this way have anything to do with the stack alignment, I know that (having the EIP overwritten) would be an issue if strcpy was called in main(), studying from other SE posts/answers

As you can see, the problem was solved but I don't understand why it worked. Please explain, or point to the write direction. Much kudos and thanks in advance.

0x5929
  • 335
  • 4
  • 13

2 Answers2

3

My guess is that you overwrote other local variables (i or sending_str) and thus caused premature crash.

You will have to look at the generated code to see what’s going on. Depending on settings the compiler can do interesting things to your code, including inlining the called function altogether, thus making your return address overwrite take effect much later, etc.

manduca
  • 1,111
  • 7
  • 10
  • Sounds right, sending_str probably was overwritten. – David Mar 21 '18 at 19:19
  • @manduca That is very interesting, but can you please explain it in details? Technically sending_str's memory is allocated on the heap, how would it be overwritten on the stack? Also I did forget to mention that the string received from client and inputted into the strcpy function is A*9000, which is bigger than any of the allocated local variable memory, shouldn't it overwrite the eip and the esp anyways since the input string is so big? – 0x5929 Mar 21 '18 at 20:18
  • @manduca Thank you, are you right, after several tests that is exactly what happened but instead with sending_str, it is with int i. sending_str just holds an address to the heap. However with int i, it is a different case, and as the input string gets bigger, the buffer is overflown into the next declared variable int i, which is only allocated a space for a type int and the extra overflown string is overwriting i, and causes an access violation when that happens, so the true exception happens before we get to instruction manipulating the eip register for the return address. – 0x5929 Mar 22 '18 at 03:42
  • It all depends on the ASM the compiler produced. In your case it seems that all optimizations are disabled and the local vars are on the stack. To your first question: While the memory sending_str points to is indeed on the heap, sending_str itself is a pointer that could be on the stack(if compiler didn´t put it into a register instead). So if that pointer is overwritten, it points somewhere else, and the next write using that pointer goes somewhere lese, possibly into unmapped mem.Once the first (=least significant on x86) byte of either pointer or counter is overwritten, things get weird. – manduca Mar 22 '18 at 09:08
  • Yes you are absolutely right, although sending_str points to the heap, the variable itself was still pushed to the stack during the beginning of the function call, and could be overwritten. However I think the error exception happens as soon as the buf gets overflown by the Huge string, when strcpy is called and depending on the location of the strcpy() call, the eip will only get overwritten at the end of the function where the eip register gets updated takes the return address from the stack which is overflown with 41414141 and the string of "A"s. – 0x5929 Mar 22 '18 at 18:41
2

Voted @manduca for best answer because the answer provided the direction of research and more testing. And after several tests, I was able to conclude the complete answer to my question asked:

  1. the type of the function return value had nothing to do with the problem, it was simply the wrong assumption
  2. for each of the two scenarios in the question, the EIP will store the memory address of the next instruction. The only time it will take an address (the return address) that has been pushed to the stack since the beginning of the stack frame creation is at the end of the function call/return statement. Therefore the exception will happen as soon as we overwrite our buf, and if the next line of instruction is NOT the end of the function (returning void) or a return statement, the EIP will not be updated from the stack, as it will just stores to the memory location of the next instruction (Which I am guessing by incrementing from the memory location of the previous instruction ). And that is why the EIP was not overwritten by our string of 10MB of "A"s. Meanwhile the solution had the strcpy function call right before either the return statement or the end of the vuln_func function call, so the EIP is updated while the stack is blowing up with the buffer overflow, which explains why the solution had EIP overwritten with our "A"s
  3. No I don't think this problem has anything to do with stack alignment, which is just adding some paddings to stack size(default 16bytes in 64bit systems), which should also be accounted for if we were trying to do string manipulation in the overflow and purposely trying to change the EIP, honestly I feel like this problem only exist in main(), so avoiding exploiting main() could be a solution.
0x5929
  • 335
  • 4
  • 13