Virtual Memory and Executables
Buffer Overflow Attacks
[Buffer overflows(https://en.wikipedia.org/wiki/Stack_buffer_overflow) on the stack are a common exploitation method. Learn the basics in this buffer overflow attack video
A typical objective of such an attack is to inject malicious code to provide the attacker with a shell. This code is called shell code.
Why stack overflows work:
- Stack fills variables down (high to low)
- Strings fill up (low to high)
- Return address at highest memory
- Buffer overrun fills up to overwrite return address
Protections: Non-executable Memory
Basic buffer overflow exploits arise from the unintended execution of (read-only) data. One protection is to explicitly designate data as non executable (NX), however even with this protection there are ways to boostrap “gadgets” out of pieces of executable code to overcome this, including return-oriented programming and return-to-libc attacks.
Protections: Address Space Layout Randomization
Recall that our buffer overlflow attack was exploited by getting the program to jump to an address of the attacker’s choosing. Recall in the passcode challenge the attacker exploits that fact that they can write to any memory address, and they use this ability to overwrite the address of
fflush in the Procedure Linkage Table. So when the program tries to call
fflush, it actually ends up calling the
cat flag system instruction instead. Uh oh!
But for this to work, the attacker needs to know the memory address to write to. Recall we found this by running:
(gdb) disas fflush Dump of assembler code for function fflush@plt: => 0x08048430 <+0>: jmp *0x804a004 0x08048436 <+6>: push $0x8 0x0804843b <+11>: jmp 0x8048410 End of assembler dump.
The attacker needs to place the address of the
cat flag instruction into address
0x804a004. Similarly we can find the address of this system call:
(gdb) disas login ... 0x080485ea <+134>: call 0x8048460 <system@plt> ...
So as you see the attack is highly dependent on the specific memory address. So if we could somehow make these addresses random, we could make such attacks much harder. Address Space Layout Randomization (ASLR) provides such protection against buffer overflows.
You can check if your system supports ASLR by typing:
A value of
0 means ASLR is not present, whereas
2 are respectively support for “conservative” and “full” randomization. In order to exploit ASLR you must compile your program to as a “position independent executable” (PIE) using the following compiler flags:
gcc -pie -fPIE <yourprogram.c>
We can test this out on the
username.c program from assignment 1:
gcc -pie -fPIE username.c -g chmod +x a.out gdb a.out
By default GDB turns off ASLR for debugging, however you can re-enable it as follows:
(gdb) set disable-randomization off
Now let’s set a breakpoint in the
main() function. Run the program and disassemble the
pin function and look for the
<system@plt> call. We get something that looks like this:
0x8005d76e <+94>: call 0x8005d510 <system@plt>
But now let’s stop the debug and run it again. We get something different, e.g.:
0x8004976e <+94>: call 0x80049510 <system@plt>
And if we run it again:
0x800eb76e <+94>: call 0x800eb510 <system@plt>
This is a problem for the attacker. If the address of the system call in memory is randomly chosen at runtime, how can the attacker know where it will be to jump to it?