Buffer Overflows

Virtual Memory and Executables

Programs are layed out in virtual memory and are organized by a memory map containing several key areas such as the stack and heap.

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:

cat /proc/sys/kernel/randomize_va_space

A value of 0 means ASLR is not present, whereas 1 and 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?