Stack Attacks

Understanding Pointers

  • Every pointer has a type
  • Every pointer has a value: 0 or NULL means it points nowhere
  • & can create a pointer by allocating space on the stack for a variable
  • * dereferences a pointer
  • The name of an array is a pointer to the first element in the array
  • a[3] is the same as *(a+3)

GDB

A great guide to help get started using GDB can be found here.

Out of Bounds Memory Reference and Buffer Overflow

The Standard C Library function gets(char *s) reads in an infinite number of characters from stdin until a new line or EOF character are read and stores the characters that are read in memory starting at address s.  The new line character, however, is not stored in memory.

The puts(const char* s) function prints to stdout the string pointed to by s.

Now, consider the following procedure.

void echo() {
    char buf[8];
    gets(buf);
    puts(buf);
}

Assembly

echo:
    subq  $24,  %rsp        // allocate 24 bytes on the stack
    movq  %rsp, %rdi        // pass the address to the top of the stack
                            // as arg1 to gets()
    call  gets
    movq  %rsp, %rdi        // pass the address to the top of the stack
                            // as arg1 to gets()
    call  puts
    addq  $24,  %rsp        // deallocate the 24 bytes on the stack
ret

Possible Corruption

Since the compiler allocated 24 bytes, rather than just 8, no harm is done if the user types in 24 or fewer characters.  But if the user enters more characters, other data that is stored on the stack will be overwritten.

# Chars Typed What is Overwritten
0-7 Nothing
9-23 Unused stack space
24-31 Return address for echo
32+ Saved state in caller

Using gets or any function that can overflow is considered bad programming practice.  Strcpy, strcat and sprintf can also cause overflow.  User methods that limit the number of bytes read or written like fgets.

A malicious use of buffer overflow

  1. Writes a string that contains the byte encoding of some executable code (exploit code)
  2. Overwrites the return address to the address of the exploit code.
  3. When the function returns it jumps to the exploit code which is executed.

Thwarting Buffer Overflow

Mechanisms of GCC on Linux

Stack Randomization

To insert exploit code the attacker must inject code onto the stack.  This is easy if the program uses functions like gets() that doesn’t limit the size of the input and if the program doesn’t validate the input.  The attacker must also replace the return address for a procedure with an address to the exploit code.

Historically, the stack addresses for a program were predictable.  If one system started a set of programs and another system started the same set, the memory addresses of each program were the same.  This allowed a hacker to build an exploit on one machine and launch it on others.  To circumvent this attack vector, kernels use stack randomization.  A randomized stack implies that programs are loaded at random places in memory.  This is accomplished by allocating a random number of bytes on the stack when a program is started.  Stack randomization is one of a larger class of techniques known as ASLR (address-space layout randomization).  With ASLR, program code, library code, stack, global variables, and heap data are loaded into different regions of memory each time a program is run.

To overcome randomization, some apply a brute force attack.  A common trick is to repeatedly execute nop (no op) instructions.  nop instructions simply advance the program counter.  This attack is called a “nop sled“.

Stack Corruption Detection

gcc incorporates a stack protector into generated code to detect buffer overruns.  Here, a random value (canary value) is placed in the stack frame between any local buffer and the rest of the stack when a function is called.  Before returning from the function, the program verifies the canary value is unchanged.  If it has, it aborts with an error.  An assembly example is in the text.

Limiting Executable Code Regions

Hardware supports memory protection as well.  Certain memory can be restricted to readable, writable and executable.  AMD and Intel chips can check if a region of memory is executable.

Variable Size Stack Frames

 

 

 

© 2017 – 2018, Eric. All rights reserved.