I am trying to test this example from StackOverflow (how-can-i-invoke-buffer-overflow), but I am not having success.
I also asked for clarification two weeks ago, directly on the post (through a comment) but there was still no answer (perhaps too old, 2010).
I am asking for a parsimoniously way of make this work: compiler options, operating system configuration, if necessary changing the code to make it compliant with today's process/memory layout or able to surpass, by the least, today's security OS protections.
I tried my own guesses but nothing seems to work.
I would like to avoid continuing doing superstitious attempts (compiler options that have nothing to do, operating system tinkering that has nothing to do) and opted to ask here if an expert or a well informed person come up with a proposal or at least points to a promising path.
My result:
$ gcc overflow.c
$ ./a.out
now inside f()!
Result supposed to happen:
nils@doofnase:~$ gcc overflow.c
nils@doofnase:~$ ./a.out
now inside f()!
now inside g()!
now inside g()!
now inside g()!
now inside g()!
now inside g()!
now inside g()!
Segmentation fault
Code:
#include <stdio.h>
#include <stdlib.h>
void g()
{
printf("now inside g()!\n");
}
void f()
{
int i;
void * buffer[1];
printf("now inside f()!\n");
// can only modify this section
// cant call g(), maybe use g (pointer to function)
// place the address of g all over the stack:
for (i=0; i<10; i++)
buffer[i] = (void*) g;
// and goodbye...
}
int main (int argc, char *argv[])
{
f();
return 0;
}
My machine:
x86_64 GNU/Linux
6.10.9-amd64
Just because you wrote past the end of buffer
doesn't necessarily mean the code will crash. That's part of undefined behavior.
What most likely happened here is that you overwrote the value of i
on the second iteration of the loop. When I ran this code I observed the following stack layout for f
:
rsp+0 | buffer[0]
rsp+8 | (padding)
rsp+12 | i
rsp+16 | main
I also noted that the local functions had addresses in the 0x400000-0x400600 range, meaning that the upper 4 bytes of these addresses is 0.
So when you write to buffer[1]
, the low-order 4 bytes of the address end up where the padding is, and the high-order 4 bytes with the value 0 overwrite i
. So i
gets reset to 0 at the end of every iteration of the loop, resulting in buffer[1]
getting repeatedly written into in an infinite loop.
If you instead do this in the loop:
if (i<2) {
buffer[i] = (void *)((uintptr_t)g | ((uintptr_t)i << 32));
} else {
buffer[i] = g;
}
This will set the high-order 4 bytes of buffer[i]
to the current value of i
for values of 0 and 1. That way the value of i
will be preserved at it progresses through the loop, then the return address of main
on the stack can be overwritten.
And by only doing this for only the first few iterations to protect the value of i
, the address of main
on the stack will get overwritten with the address of g
, allowing the code to return-to g
and execute the function.