brian e. brzezicki. this tutorial just illustrates the underlying concepts of buffer overflows by...

52
Basic theory of buffer overflows Brian E. Brzezicki

Upload: christine-powers

Post on 01-Jan-2016

218 views

Category:

Documents


0 download

TRANSCRIPT

Basic theory of buffer overflows

Brian E. Brzezicki

Just the basics

This tutorial just illustrates the underlying concepts of buffer overflows by way of an extremely simple stack overflow Most buffer overflow concepts derive from

these concepts This techniques is for basic understanding,

they are not advanced techniques This technique described as is will not

work in any modern OS due to compiler and OS protections

Key Terms

To understand buffer overflow requires understanding a few terms

IP register Function Stack

IP register

A special memory location directly on the CPU which holds the address in memory of the next instruction to be executed On Intel IA32 architectures it is called EIP On Intel IA64 architectures it is called RIP

* if an attacker can set the value of this register, they can direct the CPU to execute their instructions

Stack

A data structure in system memory where data is stored temporarily Stacks usually grow down from lower to

higher memory addresses, as data is added to the stack

Memory Address

Value

1000 First stack variable

9996

9992

Stack

A data structure in system memory where data is stored temporarily Stacks usually grow down from lower to

higher memory addresses, as data is added to the stack

Memory Address

Value

1000 First stack variable

9996 Second stack variable

9992

Stack

A data structure in system memory where data is stored temporarily Stacks usually grow down from lower to

higher memory addresses, as data is added to the stack

Memory Address

Value

1000 First stack variable

9996 Second stack variable

9992 Third stack variable

Stack

A data structure in system memory where data is stored temporarily Stacks usually grow down from lower to

higher memory addresses, as data is added to the stack

Memory Address

Value

1000 First stack variable

9996 Second stack variable

9992 Third stack variable

… …

Function

A small part of a program that performs a specific action or function Programs are comprised of many functions

main() {

char [8] string;

printf(“hi there how are you?”);

gets(string);

}

Function

A small part of a program that performs a specific action or function Programs are comprised of many functions

main() {

char [8] string;

printf(“hi there how are you?”);

gets(string);

}

* printf and gets are functions

How functions use the stack

When a function is called any parameters passed to the function are added to the stack

add(x,y);

printf(“hi there”);Memory Address

Value

1000

9996

9992

How functions use the stack

When a function is called any parameters passed to the function are added to the stack

add(x,y);

printf(“hi there”);Memory Address

Value

1000

9996

9992

How functions use the stack

When a function is called any parameters passed to the function are added to the stack

add(x,y);

printf(“hi there”);Memory Address

Value

1000 y

9996

9992

How functions use the stack

When a function is called any parameters passed to the function are added to the stack

add(x,y);

printf(“hi there”);Memory Address

Value

1000 y

9996 x

9992

How functions use the stack

After the parameters are added to the stack, the memory address of the next instruction after the function is put on the stack

add(x,y);

printf(“hi there”);Memory Address

Value

1000 y

9996 x

9992

How functions use the stack

After the parameters are added to the stack, the memory address of the next instruction after the function is put on the stack (return address)

add(x,y);

printf(“hi there”);Memory Address

Value

1000 y

9996 x

9992 address_of printf(“hi there”);

How functions use the stack

Any local variable that the function uses will be placed on the stack after the return address.

sub add(x,y) {

int total;

total=x+y;

return(total);

}

Memory Address

Value

1000 y

9996 x

9992 address_of printf(“hi there”);

How functions use the stack

Any local variable that the function uses will be placed on the stack after the return address.

sub add(x,y) {

int total;

total=x+y;

return(total);

}

Memory Address

Value

1000 y

9996 x

9992 address_of printf(“hi there”);

… total

How functions use the stack

Once the function completes, the local variables will be removed from the stack

sub add(x,y) {

int total;

total=x+y;

return(total);

}

Memory Address

Value

1000 y

9996 x

9992 address_of printf(“hi there”);

… total

How functions use the stack

Once the function completes, the local variables will be removed from the stack

sub add(x,y) {

int total;

total=x+y;

return(total);

}

Memory Address

Value

1000 y

9996 x

9992 address_of printf(“hi there”);

How functions use the stack

Finally the CPU will load the memory address that is on the stack into the IP register and continue execution at that point

sub add(x,y) {

int total;

total=x+y;

return(total);

}

Memory Address

Value

1000 y

9996 x

9992 address_of printf(“hi there”);

Finally the CPU will load the memory address that is on the stack into the IP register and continue execution at that point

sub add(x,y) {

int total;

total=x+y;

return(total);

}

How functions use the stack

Memory Address

Value

1000 y

9996 x

9992 address_of printf(“hi there”);

IP Register = address_of printf(“hi there”);

The exploit

The key of an buffer overflow is to 1. get your own code (shellcode) into

memory2. overwrite the function return

address to point to the memory location of your code

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

In this function the line above is vulnerable, as it takes any length input and tries to store it into the location assigned to input which is only 8 bytes long

The stack

Now let’s look at the memory layout of the stack

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 input variable cont

9968 input variable space

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 input variable cont

9968 input variable space

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 input variable cont

9968 input variable space

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 input variable cont

9968 input variable space

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 input variable cont

9968 input variable space

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

When the input is read from the user, the data will be stored in the space allocated for the input variable

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 input variable cont

9968 input variable space

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

When the input is read from the user, the data will be stored in the space allocated for the input variable

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 input variable cont

9968 input variable space

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

When the input is read from the user, the data will be stored in the space allocated for the input variable

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 input variable cont

9968 input variable space

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

You could enter your own code when prompted to “please enter input”

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 input variable cont

9968 input variable space

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

You could enter your own code when prompted to “please enter input”

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 input variable cont

9968 input variable space

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

You could enter your own code when prompted to “please enter input”

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 input variable cont

9968 your shellcode

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

You could enter your own code when prompted to “please enter input”

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 your shellcode

9968 your shellcode

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

If you enter more than 8 characters, you will start overwriting the other data on the stack

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 plea

9972 your shellcode

9968 your shellcode

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

If you enter more than 8 characters, you will start overwriting the other data on the stack

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 se e

9976 your shellcode

9972 your shellcode

9968 your shellcode

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

If you enter more than 8 characters, you will start overwriting the other data on the stack

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 nter

9980 your shellcode

9976 your shellcode

9972 your shellcode

9968 your shellcode

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

If you enter more than 8 characters, you will start overwriting the other data on the stack

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 inp

9984 your shellcode

9980 your shellcode

9976 your shellcode

9972 your shellcode

9968 your shellcode

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

If you enter more than 8 characters, you will start overwriting the other data on the stack

Memory Address

Value

1000 x

9996 return address

9992 ut

9988 your shellcode

9984 your shellcode

9980 your shellcode

9976 your shellcode

9972 your shellcode

9968 your shellcode

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

If you enter more than 8 characters, you will start overwriting the other data on the stack

Memory Address

Value

1000 x

9996 return address

9992 your shellcode

9988 your shellcode

9984 your shellcode

9980 your shellcode

9976 your shellcode

9972 your shellcode

9968 your shellcode

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

Now you’ve overwrote the local stack variables… if you write more you’ll overwrite the return address

Memory Address

Value

1000 x

9996 return address

9992 your shellcode

9988 your shellcode

9984 your shellcode

9980 your shellcode

9976 your shellcode

9972 your shellcode

9968 your shellcode

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

If you put the address of the start of your shellcode, when the function returns the IP will be loaded with the address of your shellcode

Memory Address

Value

1000 x

9996 9968

9992 your shellcode

9988 your shellcode

9984 your shellcode

9980 your shellcode

9976 your shellcode

9972 your shellcode

9968 your shellcode

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

If you put the address of the start of your shellcode, when the function returns the IP will be loaded with the address of your shellcode

Memory Address

Value

1000 x

9996 9968

9992 your shellcode

9988 your shellcode

9984 your shellcode

9980 your shellcode

9976 your shellcode

9972 your shellcode

9968 your shellcode

Exploitable function

interactWithUser(int x) { char msg=“please enter input”; char[8] input;

printf(“%s”,msg); gets(input); return();}

Then the system will run your shell code instead of returning to the normal program!

Memory Address

Value

1000 x

9996 return address

9992 your shellcode

9988 your shellcode

9984 your shellcode

9980 your shellcode

9976 your shellcode

9972 your shellcode

9968 your shellcode

You control the execution

Now you have Successfully input your own code in

memory Directed the system to execute your

code

Game Over… YOU win!

Additional things to think about / need to solve

How did we know where our shellcode’s address is in memory?

How do we determine the shellcode? Don’t programs generally generaly

filter input for un-allowed characters?

What happens if the system uses a Non-eXecutable stack / memory or Address Space Layout Randomization (ALSR)

references

“Smashing the Stack for Fun and Profit” Phrack issue 49 available at

http://insecure.org/stf/smashstack.html