2
votes

I have a pretty simple program for learning stack overflow.

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) {

  char buf[128];

  if(argc < 2) return 1;

  strcpy(buf, argv[1]);

  printf("Hello\n");

  return 0;
}

The strategy is to supply large string in argv[1] to overflow buf and overwrite the return address. But which return address? i thought it is the address saved before I entered strcpy, so when we return normally from strcpy, we will execute printf.

However, after I overflow the buffer with a shell code payload to change this return address to my shellcode. I see the printf is still executed. Even if I added a few more printf, they will all be executed. Apparently, the return address I change only affects the main function return, otherwise I should not even see the printfs being executed.

Why would this happen? Isn't that when I overrun the buffer to change the return address to my shellcode, the main program will jump to my shellcode directly without executing the next printf?

3
The address of printf is never on the stack, so how could you overwrite it?melpomene
@melpomene, I mean before I call strcpy, I save the return address. Then strcpy corrupts that return address since I give a large string. When strcpy returns, it jumps to my shellcode and should not execute printf at alldrdot
If your stack grows downward, you can't mess with strcpy's stack frame because it's before buf in memory.melpomene
@melpomene, I see your point. Buf lives in main. If you write that as an answer, I will accept it.drdot
this is a buffer overflow, not a stackoverflow.Daniel A. White

3 Answers

2
votes

On your typical PC, the stack grows downward. That means the memory layout of the stack will look like this while calling strcpy:

// ^^^ higher addresses ^^^
[stuff]
[return address of main]
[buf[127]]
[buf[126]]
...
[buf[1]]
[buf[0]]
[argument 2 (pointer to argv[1])]
[argument 1 (pointer to buf)]
[return address of strcpy (points into main)]
[local variables in strcpy]
// vvv lower addresses vvv

By overflowing buf (writing to buf[128], buf[129], etc.), you write over main's call frame (most importantly, main's return address). You can't affect strcpy's call frame because it lives before buf in memory.

0
votes

When the program execution enters main, the return address from main is on the stack. Next, 128 bytes are allocated for buf. The strcpy call, with a second argument longer than 128 bytes, scribbles beyond the space allocated for buf, and presumably corrupting the return address.

Next, the arguments for printf and return address (pointing to the statement after the printf) are pushed onto the stack, and the execution jumps to the printf function. After performing the requested printing, the return address is popped of the stack, and execution resumes with the next statement.

Finally, the return 0; statement is reached. The space allocated to local variables on the stack is recovered, and the (corrupted) return address is popped off the stack, and execution "returns" to that corrupted address given by the bytes of the string used to corrupt the memory.

In short, the buffer overflow can only scribble over information already written on the stack (the past). It cannot scribble over information that has not yet been written to the stack (the future). This is why printf calls after the corruption still execute properly; the corrupted data hasn't been used yet.

Stack, at strcpy call:

xxxx
xxxx
return address from strcpy
&buf    (arg to strcpy)
argv[1] (arg to strcpy)
buf[0]
...
buf[127]
return address from main
argc     (arg to main)
argv[0]  (arg to main)
argv[1]  (arg to main)
...

The damage is done after buf[127]. The return from strcpy is not damaged.

Note: It is also possible that there is no "return from strcpy" on the stack; the compiler may have inlined the function call.

-2
votes

buf is a variable of a function, so it lives on the stack. Your buffer overflow will corrupt the stack