Alright, I've been looking into buffer overflows lately out of curiosity. What I don't understand is when you develop the exploit with a virtual machine or whatever, you find the memory address to overflow the instruction pointer with and bam, your exploit works. What I do not get is how does that exploit work on another machine? Wouldn't that memory address be completely different?
1 Answer
Yes and no. The memory addresses that you see from a process do not directly correspond to the physical (or even virtual) memory. One of the tasks of the kernel is to map the memory of each process and give that process the illusion that it is the only process running on a machine.
Therefore all processes start their .text
(and other parts of code and static libraries) from memory close to 0x0
and their stack starts from the end of the memory. (That's a actually more complex than that, but it is a decent enough analogy. Moreover, since malloc()
is a system call the kernel will tell the process an out of memory issue no matter how much memory the process sees). If you start the same process with the same input two times, its memory shall look the same on both starts.
The final mapping between the memory seen by a process and the physical memory is done by the CPU by the use of mapping tables (of which the CPU is told by means of a register).
On the other hand it isn't that easy. Seeing the problem with the possibility of the overflow using the same memory address stack randomization (ASLR) came into life. Several Operating Systems today enable stack randomization by default, where the kernel do not start the memory of a process from 0x0
but from a random initial value. This makes the memory of the process to be shifted by a handful of bytes even when the same process starts two times.
Example
The following trivial program:
#include <stdio.h>
int main(void) {
int i;
printf("%p\n", &i);
return 0;
}
When run on a machine with stack randomization (ASLR) enabled will print distinct addresses, but when I disable it it starts printing the same addresses (assuming the file with the program is named c.c
):
[~]$ gcc -o c c.c
[~]$ ./c
0x7ffd52d0aa3c
[~]$ ./c
0x7ffc27da904c
[~]$ sudo echo 0 > /proc/sys/kernel/randomize_va_space
[~]$ ./c
0x7fffffffe86c
[~]$ ./c
0x7fffffffe86c
Extra Note
After testing this on two very similar machines (x86_64 linux 4.8.13) I got exactly the same output from the last two lines. That is because the program and the kernel are deterministic.
Yet, that may not be the case across very different versions of the kernel. For example, kernel 2.x will likely produce a different address for i
. This means that the VM where an exploit is developed must mirror the target machine as much as possible (hence the danger of machine fingerprinting).
-
Thanks :) I've been looking for an answer online but this is much clearer than anything I've found– GenthornCommented Dec 20, 2016 at 1:44