02 Buffer PDF

Download as pdf or txt
Download as pdf or txt
You are on page 1of 229

CLASSIC


MEMORY ATKS & DEFS


CMSC 414
JAN 30 2018
TODAY’S RESOURCES
REFRESHER
• How is program data laid out in memory?

• What does the stack look like?

• What effect does calling (and returning from) a


function have on memory?

• We are focusing on the Linux process model


• Similar to other operating systems
ALL PROGRAMS ARE STORED IN MEMORY
4G

0
ALL PROGRAMS ARE STORED IN MEMORY
4G 0xffffffff

0 0x00000000
ALL PROGRAMS ARE STORED IN MEMORY
4G 0xffffffff

The process’s view


of memory is that
it owns all of it

0 0x00000000
ALL PROGRAMS ARE STORED IN MEMORY
4G 0xffffffff

The process’s view In reality, these are


of memory is that virtual addresses;
it owns all of it the OS/CPU map
them to physical
addresses

0 0x00000000
THE INSTRUCTIONS THEMSELVES ARE STORED IN MEMORY

4G 0xffffffff

Text

0 0x00000000
THE INSTRUCTIONS THEMSELVES ARE STORED IN MEMORY

4G 0xffffffff

...
0x4c2 sub $0x224,%esp
0x4c1 push %ecx
0x4bf mov %esp,%ebp
0x4be push %ebp
...
Text

0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff

Text

0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff

Init’d data static const int y=10;

Text

0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff

Uninit’d data static int x;

Init’d data static const int y=10;

Text

0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff

Uninit’d data static int x;


Known at Init’d data static const int y=10;
compile time
Text

0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff
cmdline & env

Uninit’d data static int x;


Known at Init’d data static const int y=10;
compile time
Text

0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff
Set when

cmdline & env
process starts

Uninit’d data static int x;


Known at Init’d data static const int y=10;
compile time
Text

0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff
Set when

cmdline & env
process starts int f() {

Stack int x;

Uninit’d data static int x;


Known at Init’d data static const int y=10;
compile time
Text

0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff
Set when

cmdline & env
process starts int f() {

Stack int x;

Heap malloc(sizeof(long));

Uninit’d data static int x;


Known at Init’d data static const int y=10;
compile time
Text

0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff
Set when

cmdline & env
process starts int f() {

Stack int x;
Dynamically
 …

sized at runtime
Heap malloc(sizeof(long));

Uninit’d data static int x;


Known at Init’d data static const int y=10;
compile time
Text

0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff
Set when

cmdline & env
process starts int f() {

Stack int x;
Dynamically
 …

sized at runtime
Heap malloc(sizeof(long));

Uninit’d data static int x;


Known at Init’d data static const int y=10;
compile time
Text

0 0x00000000
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

0x00000000 0xffffffff
Heap Stack
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap Stack
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap Stack

Stack
pointer
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap Stack

Stack push 1

push 2

pointer push 3
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap Stack

Stack push 1

push 2

pointer push 3
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap Stack

Stack push 1

pointer push 2

push 3
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap 1 Stack

Stack push 1

pointer push 2

push 3
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap 1 Stack

Stack push 1

pointer push 2

push 3
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap 2 1 Stack

Stack push 1

pointer push 2

push 3
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap 2 1 Stack

Stack push 1

pointer push 2

push 3
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap 3 2 1 Stack

Stack push 1

pointer push 2

push 3
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap 3 2 1 Stack

Stack push 1

pointer push 2

push 3
return
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap 3 2 1 Stack

Stack push 1

pointer push 2

push 3
return
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap 3 2 1 Stack
{
apportioned by the OS; Stack push 1

managed in-process pointer push 2

push 3
by malloc return
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions

Compiler provides instructions that



adjusts the size of the stack at runtime

0x00000000 0xffffffff
Heap 3 2 1 Stack
{
apportioned by the OS; Stack push 1

managed in-process pointer push 2

push 3
by malloc return

Focusing on the stack for now


STACK LAYOUT WHEN CALLING FUNCTION
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}

0x00000000 0xffffffff
caller’s data
STACK LAYOUT WHEN CALLING FUNCTION
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}

0x00000000 0xffffffff
arg1 arg2 arg3 caller’s data

Arguments

pushed in

reverse order
of code
STACK LAYOUT WHEN CALLING FUNCTION
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}

0x00000000 0xffffffff
… loc2 loc1 arg1 arg2 arg3 caller’s data
Local variables
 Arguments

pushed in the pushed in

same order as reverse order
they appear
 of code
in the code
STACK LAYOUT WHEN CALLING FUNCTION
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data
Local variables
 Arguments

pushed in the pushed in

same order as reverse order
they appear
 of code
in the code
STACK LAYOUT WHEN CALLING FUNCTION
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}
Two values between the arguments

0x00000000 and the local variables 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data
Local variables
 Arguments

pushed in the pushed in

same order as reverse order
they appear
 of code
in the code
STACK LAYOUT WHEN CALLING FUNCTION
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}

0x00000000 0xffffffff
caller’s data
STACK LAYOUT WHEN CALLING FUNCTION
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}

0x00000000 0xffffffff
arg1 arg2 arg3 caller’s data
Arguments

pushed in

reverse order
of code
STACK LAYOUT WHEN CALLING FUNCTION
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}

0x00000000 0xffffffff
loc2 loc1 arg1 arg2 arg3 caller’s data
Local variables
 Arguments

pushed in the pushed in

same order as reverse order
they appear
 of code
in the code
STACK LAYOUT WHEN CALLING FUNCTION
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}

0x00000000 0xffffffff
loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data
Local variables
 Arguments

pushed in the pushed in

same order as reverse order
they appear
 of code
in the code
STACK LAYOUT WHEN CALLING FUNCTION
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}
Two values between the arguments

0x00000000 and the local variables 0xffffffff
… loc3 loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data …
Local variables
 Arguments

pushed in the pushed in

same order as reverse order
they appear
 of code
in the code
STACK FRAMES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}

0x00000000 0xffffffff
caller’s data
STACK FRAMES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}

0x00000000 0xffffffff
arg1 arg2 arg3 caller’s data
STACK FRAMES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}

0x00000000 0xffffffff
loc2 loc1 arg1 arg2 arg3 caller’s data
STACK FRAMES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
...
}

0x00000000 0xffffffff
… loc3 loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data …

The part of the stack corresponding to



this particular invocation of

this particular function
STACK FRAMES
void main() { countUp(3); }

void countUp(int n) {

if(n > 1)
countUp(n-1);
printf(“%d\n”, n);
}

0x00000000 0xffffffff
main( ) …
STACK FRAMES
void main() { countUp(3); }

void countUp(int n) {

if(n > 1)
countUp(n-1);
printf(“%d\n”, n);
}

0x00000000 0xffffffff
main( ) …

Stack
pointer
STACK FRAMES
void main() { countUp(3); }

void countUp(int n) {

if(n > 1)
countUp(n-1);
printf(“%d\n”, n);
}

0x00000000 0xffffffff
countUp(3) main( ) …

Stack
pointer
STACK FRAMES
void main() { countUp(3); }

void countUp(int n) {

if(n > 1)
countUp(n-1);
printf(“%d\n”, n);
}

0x00000000 0xffffffff
countUp(2) countUp(3) main( ) …

Stack
pointer
STACK FRAMES
void main() { countUp(3); }

void countUp(int n) {

if(n > 1)
countUp(n-1);
printf(“%d\n”, n);
}

0x00000000 0xffffffff
countUp(1) countUp(2) countUp(3) main( ) …

Stack
pointer
STACK FRAMES
void main() { countUp(3); }

void countUp(int n) {

if(n > 1)
countUp(n-1);
printf(“%d\n”, n);
}

0x00000000 0xffffffff
countUp(1) countUp(2) main( ) …

Stack
pointer
STACK FRAMES
void main() { countUp(3); }

void countUp(int n) {

if(n > 1)
countUp(n-1);
printf(“%d\n”, n);
}

0x00000000 0xffffffff
countUp(1) main( ) …

Stack
pointer
STACK FRAMES
void main() { countUp(3); }

void countUp(int n) {

if(n > 1)
countUp(n-1);
printf(“%d\n”, n);
}

0x00000000 0xffffffff
main( ) …

Stack
pointer
ACCESSING VARIABLES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2;
int loc3;
loc2++;
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data
ACCESSING VARIABLES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2; Q: Where is (this) loc2?
int loc3;
loc2++;
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data
ACCESSING VARIABLES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2; Q: Where is (this) loc2?
int loc3;
loc2++;
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

0xbffff323
ACCESSING VARIABLES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2; Q: Where is (this) loc2?
int loc3;
loc2++;
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

0xbffff323
Undecidable at

compile time
ACCESSING VARIABLES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2; Q: Where is (this) loc2?
int loc3;
loc2++;
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

0xbffff323
Undecidable at
 - I don’t know where loc2 is,
compile time
ACCESSING VARIABLES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2; Q: Where is (this) loc2?
int loc3;
loc2++;
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

Variable args?
0xbffff323
Undecidable at
 - I don’t know where loc2 is,
compile time - and I don’t know how many args
ACCESSING VARIABLES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2; Q: Where is (this) loc2?
int loc3;
loc2++;
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

4B 4B 4B 4B Variable args?
0xbffff323
Undecidable at
 - I don’t know where loc2 is,
compile time - and I don’t know how many args
ACCESSING VARIABLES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2; Q: Where is (this) loc2?
int loc3;
loc2++;
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

4B 4B 4B 4B Variable args?
0xbffff323
Undecidable at
 - I don’t know where loc2 is,
compile time - and I don’t know how many args
- but loc2 is always 8B before “???”s
ACCESSING VARIABLES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2; Q: Where is (this) loc2?
int loc3;
loc2++;
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

- I don’t know where loc2 is,


- and I don’t know how many args
- but loc2 is always 8B before “???”s
ACCESSING VARIABLES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2; Q: Where is (this) loc2?
int loc3;
loc2++;
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

%ebp
Frame pointer - I don’t know where loc2 is,
- and I don’t know how many args
- but loc2 is always 8B before “???”s
ACCESSING VARIABLES
void func(char *arg1, int arg2, int arg3)
{
char loc1[4]
int loc2; Q: Where is (this) loc2?
int loc3;
loc2++;
A: -8(%ebp)
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

%ebp
Frame pointer - I don’t know where loc2 is,
- and I don’t know how many args
- but loc2 is always 8B before “???”s
NOTATION
%ebp A memory address

(%ebp) The value at memory address %ebp



(like dereferencing a pointer)
NOTATION
%ebp A memory address

(%ebp) The value at memory address %ebp



(like dereferencing a pointer)

0x00000000 0xffffffff
NOTATION
0xbfff03b8 %ebp A memory address

(%ebp) The value at memory address %ebp



(like dereferencing a pointer)

0x00000000 0xffffffff
NOTATION
0xbfff03b8 %ebp A memory address

(%ebp) The value at memory address %ebp



(like dereferencing a pointer)

0xbfff03b8

0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address

0xbfff0720 (%ebp) The value at memory address %ebp



(like dereferencing a pointer)

0xbfff03b8
0xbfff0720
0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address

0xbfff0720 (%ebp) The value at memory address %ebp



(like dereferencing a pointer)
pushl %ebp

0xbfff03b8
0xbfff0720
0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address

0xbfff0720 (%ebp) The value at memory address %ebp



(like dereferencing a pointer)
pushl %ebp

%esp
0xbfff03b8
0xbfff0720
0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address

0xbfff0720 (%ebp) The value at memory address %ebp



(like dereferencing a pointer)
pushl %ebp

%esp
0xbfff03b8
0xbfff0720
0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address

0xbfff0720 (%ebp) The value at memory address %ebp



(like dereferencing a pointer)
pushl %ebp

%esp
0xbfff03b8
0xbfff0720
0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address

0xbfff0720 (%ebp) The value at memory address %ebp



(like dereferencing a pointer)
pushl %ebp

%esp
0xbfff03b8
0xbfff03b8 0xbfff0720
0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address

0xbfff0720 (%ebp) The value at memory address %ebp



(like dereferencing a pointer)
pushl %ebp
movl %esp %ebp /* %ebp = %esp */

%esp
0xbfff03b8
0xbfff03b8 0xbfff0720
0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address

0xbfff0720 (%ebp) The value at memory address %ebp



(like dereferencing a pointer)
pushl %ebp
movl %esp %ebp /* %ebp = %esp */

%esp
0xbfff03b8
0xbfff03b8 0xbfff0720
0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address

0xbfff0720 (%ebp) The value at memory address %ebp



(like dereferencing a pointer)
pushl %ebp
movl %esp %ebp /* %ebp = %esp */

0xbfff0200 %esp
0xbfff03b8
0xbfff03b8 0xbfff0720
0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address
0xbfff0200
0xbfff0720 (%ebp) The value at memory address %ebp

(like dereferencing a pointer)
pushl %ebp
movl %esp %ebp /* %ebp = %esp */

0xbfff0200 %esp
0xbfff03b8
0xbfff03b8 0xbfff0720
0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address
0xbfff0200
0xbfff0720 (%ebp) The value at memory address %ebp

0xbfff03b8
(like dereferencing a pointer)
pushl %ebp
movl %esp %ebp /* %ebp = %esp */

0xbfff0200 %esp
0xbfff03b8
0xbfff03b8 0xbfff0720
0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address
0xbfff0200
0xbfff0720 (%ebp) The value at memory address %ebp

0xbfff03b8
(like dereferencing a pointer)
pushl %ebp
movl %esp %ebp /* %ebp = %esp */

movl (%ebp) %ebp /* %ebp = (%ebp) */

0xbfff0200 %esp
0xbfff03b8
0xbfff03b8 0xbfff0720
0x00000000 0xffffffff

%ebp
NOTATION
0xbfff03b8 %ebp A memory address
0xbfff0200
0xbfff0720 (%ebp) The value at memory address %ebp

0xbfff03b8
(like dereferencing a pointer)
pushl %ebp
movl %esp %ebp /* %ebp = %esp */

movl (%ebp) %ebp /* %ebp = (%ebp) */

0xbfff0200 %esp
0xbfff03b8
0xbfff03b8 0xbfff0720
0x00000000 0xffffffff

%ebp
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
...
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

Stack frame

%ebp for this call to func
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
...
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

Stack frame

%ebp for this call to func
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
...
}

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

Stack frame

%ebp for this call to func %ebp
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
...
} Q: How do we restore %ebp?

0x00000000 0xffffffff
… loc2 loc1 ??? ??? arg1 arg2 arg3 caller’s data

Stack frame

%ebp for this call to func %ebp
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
...
} Q: How do we restore %ebp?

0x00000000 0xffffffff
… ??? arg1 arg2 arg3 caller’s data

Stack frame

for this call to func %ebp
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
...
} Q: How do we restore %ebp?

0x00000000 %esp 0xffffffff


… ??? arg1 arg2 arg3 caller’s data

Stack frame

for this call to func %ebp
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
...
} Q: How do we restore %ebp?

0x00000000 %esp 0xffffffff


… %ebp ??? arg1 arg2 arg3 caller’s data

Stack frame

for this call to func %ebp
1. Push %ebp before locals
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
...
} Q: How do we restore %ebp?

0x00000000 %esp 0xffffffff


… %ebp ??? arg1 arg2 arg3 caller’s data

Stack frame

%ebp for this call to func %ebp
1. Push %ebp before locals
2. Set %ebp to current %esp
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
...
} Q: How do we restore %ebp?

0x00000000 %esp 0xffffffff


… %ebp ??? arg1 arg2 arg3 caller’s data

Stack frame

%ebp for this call to func %ebp
1. Push %ebp before locals
2. Set %ebp to current %esp
3. Set %ebp to(%ebp) at return
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
...
}

0x00000000 0xffffffff
… loc2 loc1 %ebp ??? arg1 arg2 arg3 caller’s data

Stack frame

%ebp for this call to func %ebp
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);

}
...
Q: How do we resume here?

0x00000000 0xffffffff
… loc2 loc1 %ebp ??? arg1 arg2 arg3 caller’s data

Stack frame

%ebp for this call to func %ebp
INSTRUCTIONS THEMSELVES ARE IN MEMORY
4G 0xffffffff

...
0x4a7 mov $0x0,%eax
0x4a2 call <func>
0x49b movl $0x804..,(%esp)
0x493 movl $0xa,0x4(%esp)
...
Text

0 0x00000000
INSTRUCTIONS THEMSELVES ARE IN MEMORY
4G 0xffffffff

...
0x4a7 mov $0x0,%eax
0x4a2 call <func>
0x49b movl $0x804..,(%esp)
0x493 movl $0xa,0x4(%esp) %eip
...
Text

0 0x00000000
INSTRUCTIONS THEMSELVES ARE IN MEMORY
4G 0xffffffff

...
0x4a7 mov $0x0,%eax
0x4a2 call <func>
0x49b movl $0x804..,(%esp) %eip
0x493 movl $0xa,0x4(%esp)
...
Text

0 0x00000000
INSTRUCTIONS THEMSELVES ARE IN MEMORY
4G 0xffffffff

...
0x4a7 mov $0x0,%eax
0x4a2 call <func> %eip
0x49b movl $0x804..,(%esp)
0x493 movl $0xa,0x4(%esp)
...
Text

0 0x00000000
INSTRUCTIONS THEMSELVES ARE IN MEMORY
4G 0xffffffff
...
0x5bf mov %esp,%ebp
0x5be push %ebp
...

...
0x4a7 mov $0x0,%eax
0x4a2 call <func> %eip
0x49b movl $0x804..,(%esp)
0x493 movl $0xa,0x4(%esp)
...
Text

0 0x00000000
INSTRUCTIONS THEMSELVES ARE IN MEMORY
4G 0xffffffff
...
0x5bf mov %esp,%ebp
0x5be push %ebp %eip
...

...
0x4a7 mov $0x0,%eax
0x4a2 call <func>
0x49b movl $0x804..,(%esp)
0x493 movl $0xa,0x4(%esp)
...
Text

0 0x00000000
INSTRUCTIONS THEMSELVES ARE IN MEMORY
4G 0xffffffff
... %eip
0x5bf mov %esp,%ebp
0x5be push %ebp
...

...
0x4a7 mov $0x0,%eax
0x4a2 call <func>
0x49b movl $0x804..,(%esp)
0x493 movl $0xa,0x4(%esp)
...
Text

0 0x00000000
INSTRUCTIONS THEMSELVES ARE IN MEMORY
4G 0xffffffff
...
0x5bf mov %esp,%ebp
0x5be push %ebp
...

...
0x4a7 mov $0x0,%eax %eip
0x4a2 call <func>
0x49b movl $0x804..,(%esp)
0x493 movl $0xa,0x4(%esp)
...
Text

0 0x00000000
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
... Q: How do we resume here?
}

0x00000000 0xffffffff
… loc2 loc1 %ebp ??? arg1 arg2 arg3 caller’s data

Stack frame

%ebp for this call to func %ebp
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
... Q: How do we resume here?
}

0x00000000 0xffffffff
… loc2 loc1 %ebp ??? arg1 arg2 arg3 caller’s data

Stack frame

%ebp for this call to func %ebp

Push next %eip



before call
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
... Q: How do we resume here?
}

0x00000000 0xffffffff
… loc2 loc1 %ebp %eip
??? arg1 arg2 arg3 caller’s data

Stack frame

%ebp for this call to func %ebp

Push next %eip



before call
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
... Q: How do we resume here?
}

0x00000000 0xffffffff
… loc2 loc1 %ebp %eip
??? arg1 arg2 arg3 caller’s data

Stack frame

%ebp for this call to func %ebp

Set %eip to 4(%ebp) Push next %eip



at return before call
RETURNING FROM A FUNCTION
In C In compiled assembly
leave: mov %esp %ebp
return; pop %ebp
ret: pop %eip

Current stack frame Caller’s stack frame

text ... loc2 loc1 %ebp %eip arg1

%eip %esp %ebp


RETURNING FROM A FUNCTION
In C In compiled assembly
leave: mov %esp %ebp
return; pop %ebp
ret: pop %eip

Current stack frame Caller’s stack frame

text ... loc2 loc1 %ebp %eip arg1

%eip %esp %ebp


Old frame pointer
RETURNING FROM A FUNCTION
In C In compiled assembly
leave: mov %esp %ebp
return; pop %ebp
ret: pop %eip

Caller’s code
Current stack frame Caller’s stack frame

text ... loc2 loc1 %ebp %eip arg1

%eip %esp %ebp


Old frame pointer
RETURNING FROM A FUNCTION
In C In compiled assembly
leave: mov %esp %ebp
return; pop %ebp
ret: pop %eip

Caller’s code
Current stack frame Caller’s stack frame

text ... loc2 loc1 %ebp %eip arg1

%eip %esp %ebp


Old frame pointer
RETURNING FROM A FUNCTION
In C In compiled assembly
leave: mov %esp %ebp
return; pop %ebp
ret: pop %eip

Current stack frame Caller’s stack frame

text ... loc2 loc1 %ebp %eip arg1

%eip %ebp

%esp
RETURNING FROM A FUNCTION
In C In compiled assembly
leave: mov %esp %ebp
return; pop %ebp
ret: pop %eip

Current stack frame Caller’s stack frame

text ... loc2 loc1 %ebp %eip arg1

%eip %ebp %esp


RETURNING FROM A FUNCTION
In C In compiled assembly
leave: mov %esp %ebp
return; pop %ebp
ret: pop %eip

Current stack frame Caller’s stack frame

text ... loc2 loc1 %ebp %eip arg1

%eip %esp %ebp


RETURNING FROM A FUNCTION
In C In compiled assembly
leave: mov %esp %ebp
return; pop %ebp
ret: pop %eip

Current stack frame Caller’s stack frame

text ... loc2 loc1 %ebp %eip arg1

%eip %esp %ebp


RETURNING FROM A FUNCTION
In C In compiled assembly
leave: mov %esp %ebp
return; pop %ebp
ret: pop %eip

Current stack frame Caller’s stack frame

text ... loc2 loc1 %ebp %eip arg1

%eip %esp %ebp


The next instruction is to “remove”

the arguments off the stack
RETURNING FROM A FUNCTION
In C In compiled assembly
leave: mov %esp %ebp
return; pop %ebp
ret: pop %eip

Current stack frame Caller’s stack frame

text ... loc2 loc1 %ebp %eip arg1

%eip %esp %ebp


The next instruction is to “remove”
 And now we’re

the arguments off the stack back where we started
STACK & FUNCTIONS: SUMMARY
STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after
control returns to you: e.g., %eip + 2
3.Jump to the function’s address
STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after
control returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp
STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after
control returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip
STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after
control returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

Calling function (after return):


9.Remove the arguments off of the stack: %esp = %esp + number of bytes of args
BUFFER OVERFLOW
ATTACKS
BUFFER OVERFLOWS: HIGH LEVEL
• Buffer =
• Contiguous set of a given data type
• Common in C
- All strings are buffers of char’s

• Overflow =
• Put more into the buffer than it can hold

• Where does the extra data go?

• Well now that you’re experts in memory layouts…


A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

&arg1
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

%eip &arg1
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

%ebp %eip &arg1


A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

00 00 00 00 %ebp %eip &arg1


buffer
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

00
A 00
u 00
t 00
h %ebp %eip &arg1
buffer
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

M e ! \0

00
A 00
u 00
t 00
h 4d %ebp
65 21 00 %eip &arg1
buffer
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

Upon return, sets %ebp to 0x0021654d


M e ! \0

00
A 00
u 00
t 00
h 4d %ebp
65 21 00 %eip &arg1
buffer
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

Upon return, sets %ebp to 0x0021654d


M e ! \0

00
A 00
u 00
t 00
h 4d %ebp
65 21 00 %eip &arg1
buffer SEGFAULT (0x00216551)
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
int authenticated = 0;
char buffer[4];
strcpy(buffer, arg1);
if(authenticated) { ...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
int authenticated = 0;
char buffer[4];
strcpy(buffer, arg1);
if(authenticated) { ...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
int authenticated = 0;
char buffer[4];
strcpy(buffer, arg1);
if(authenticated) { ...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

&arg1
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
int authenticated = 0;
char buffer[4];
strcpy(buffer, arg1);
if(authenticated) { ...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

%eip &arg1
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
int authenticated = 0;
char buffer[4];
strcpy(buffer, arg1);
if(authenticated) { ...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

%ebp %eip &arg1


A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
int authenticated = 0;
char buffer[4];
strcpy(buffer, arg1);
if(authenticated) { ...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

00 00 00 00 %ebp %eip &arg1


authenticated
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
int authenticated = 0;
char buffer[4];
strcpy(buffer, arg1);
if(authenticated) { ...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

00 00 00 00 00 00 00 00 %ebp %eip &arg1


buffer authenticated
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
int authenticated = 0;
char buffer[4];
strcpy(buffer, arg1);
if(authenticated) { ...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

00
A 00
u 00 h 00 00 00 00
t 00 %ebp %eip &arg1
buffer authenticated
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
int authenticated = 0;
char buffer[4];
strcpy(buffer, arg1);
if(authenticated) { ...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

M e ! \0

00
A 00
u 00 h 4d
t 00 00 65
00 21
00 00 %ebp %eip &arg1
buffer authenticated
A BUFFER OVERFLOW EXAMPLE
void func(char *arg1)
{
int authenticated = 0;
char buffer[4];
strcpy(buffer, arg1);
if(authenticated) { ...
}

int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}

Code still runs; user now ‘authenticated’


M e ! \0

00
A 00
u 00 h 4d
t 00 00 65
00 21
00 00 %ebp %eip &arg1
buffer authenticated
void vulnerable()
{
char buf[80];
gets(buf);
}
void vulnerable()
{
char buf[80];
gets(buf);
}

void still_vulnerable()
{
char *buf = malloc(80);
gets(buf);
}
void safe()
{
char buf[80];
fgets(buf, 64, stdin);
}
void safe()
{
char buf[80];
fgets(buf, 64, stdin);
}

void safer()
{
char buf[80];
fgets(buf, sizeof(buf), stdin);
}
USER-SUPPLIED STRINGS
• In these examples, we were providing our own strings

• But they come from users in myriad aways


• Text input
• Network packets
• Environment variables
• File input…
WHAT’S THE WORST THAT CAN HAPPEN?
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

00 00 00 00 %ebp %eip &mystr


buffer
WHAT’S THE WORST THAT CAN HAPPEN?
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

00 00 00 00 %ebp %eip &mystr


buffer

strcpy will let you write as much as you want (til a ‘\0’)
WHAT’S THE WORST THAT CAN HAPPEN?
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

All ours!
00 00 00 00 %ebp %eip &mystr
buffer

strcpy will let you write as much as you want (til a ‘\0’)
WHAT’S THE WORST THAT CAN HAPPEN?
void func(char *arg1)
{
char buffer[4];
strcpy(buffer, arg1);
...
}

All ours!
00 00 00 00 %ebp %eip &mystr
buffer

strcpy will let you write as much as you want (til a ‘\0’)

What could you write to memory to wreak havoc?


FIRST A RECAP: ARGS
#include <stdio.h>

void func(char *arg1, int arg2, int arg3)


{
printf(“arg1 is at %p\n”, &arg1);
printf(“arg2 is at %p\n”, &arg2);
printf(“arg3 is at %p\n”, &arg3);
}

int main()
{
func(“Hello”, 10, -3);
return 0;
}
FIRST A RECAP: ARGS
#include <stdio.h>

void func(char *arg1, int arg2, int arg3)


{
printf(“arg1 is at %p\n”, &arg1);
printf(“arg2 is at %p\n”, &arg2);
printf(“arg3 is at %p\n”, &arg3);
}

int main()
{
func(“Hello”, 10, -3);
return 0;
}

What will happen?


&arg1 < &arg2 < &arg3? &arg1 > &arg2 > &arg3?
FIRST A RECAP: LOCALS
#include <stdio.h>

void func()
{
char loc1[4];
int loc2;
int loc3;
printf(“loc1 is at %p\n”, &loc1);
printf(“loc2 is at %p\n”, &loc2);
printf(“loc3 is at %p\n”, &loc3);
}

int main()
{
func();
return 0;
}
FIRST A RECAP: LOCALS
#include <stdio.h>

void func()
{
char loc1[4];
int loc2;
int loc3;
printf(“loc1 is at %p\n”, &loc1);
printf(“loc2 is at %p\n”, &loc2);
printf(“loc3 is at %p\n”, &loc3);
}

int main()
{
func();
return 0;
}

What will happen?


&loc1 < &loc2 < &loc3? &loc1 > &loc2 > &loc3?
STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code caller’s data


STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code arg2 caller’s data


STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code arg1 arg2 caller’s data


STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code %eip+… arg1 arg2 caller’s data


STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code %eip+… arg1 arg2 caller’s data


STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code %eip+… arg1 arg2 caller’s data


STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code %ebp %eip+… arg1 arg2 caller’s data


STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code %ebp %eip+… arg1 arg2 caller’s data


STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code %ebp %eip+… arg1 arg2 caller’s data


STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code loc1 %ebp %eip+… arg1 arg2 caller’s data


STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code loc2 loc1 %ebp %eip+… arg1 arg2 caller’s data


STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code loc2 loc1 %ebp %eip+… arg1 arg2 caller’s data


STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after control
returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

%eip %ebp

~0x0
0x0

code loc2 loc1 %ebp %eip+… arg1 arg2 caller’s data


GDB: YOUR NEW BEST FRIEND
Run the program with input as the

run <input>
command-line arguments

print <var>
 Print the value of variable var



(or just “p <var>”) (Can also do some operations: p &x)

b <function> Set a breakpoint at function

s
 step through execution (into calls)



c continue execution (no more stepping)
GDB: YOUR NEW BEST FRIEND
info frame
 Show info about the current frame

(or just “i f”) (prev. frame, locals/args, %ebp/%eip)

info reg
 Show info about registers



(or just “i r”) (%ebp, %eip, %esp, etc.)

Examine <n> bytes of memory



x/<n> <addr>
starting at address <addr>
BUFFER OVERFLOW

char loc1[4];
code loc2 loc1 %ebp %eip+… arg1 arg2 caller’s data
BUFFER OVERFLOW

char loc1[4];
code loc2 loc1 %ebp %eip+… arg1 arg2 caller’s data

gets(loc1);

strcpy(loc1, <user input>);
memcpy(loc1, <user input>);
etc.
BUFFER OVERFLOW

char loc1[4];
code loc2 loc1 Input
%ebpwrites fromarg1
%eip+… arg2addresses
low to high caller’s data

gets(loc1);

strcpy(loc1, <user input>);
memcpy(loc1, <user input>);
etc.
BUFFER OVERFLOW

char loc1[4];
code loc2 loc1 %ebp %eip+… arg1 arg2 caller’s data

Input writes from low to high addresses

gets(loc1);

strcpy(loc1, <user input>);
memcpy(loc1, <user input>);
etc.
BUFFER OVERFLOW
Can over-write other data (“AuthMe!”)

char loc1[4];
code loc2 loc1 %ebp %eip+… arg1 arg2 caller’s data

Input writes from low to high addresses

gets(loc1);

strcpy(loc1, <user input>);
memcpy(loc1, <user input>);
etc.
BUFFER OVERFLOW
Can over-write other data (“AuthMe!”)
Can over-write the program’s control flow (%eip)

char loc1[4];
code loc2 loc1 %ebp %eip+… arg1 arg2 caller’s data

Input writes from low to high addresses

gets(loc1);

strcpy(loc1, <user input>);
memcpy(loc1, <user input>);
etc.
CODE
INJECTION
HIGH-LEVEL IDEA
void func(char *arg1)
{
char buffer[4];
sprintf(buffer, arg1);
...
}

... 00 00 00 00 %ebp %eip &arg1 …


buffer
HIGH-LEVEL IDEA
void func(char *arg1)
{
char buffer[4];
sprintf(buffer, arg1);
...
}

... 00 00 00 00 %ebp %eip &arg1 … Haxx0r c0d3


buffer

(1) Load our own code into memory


HIGH-LEVEL IDEA
void func(char *arg1)
{
char buffer[4];
sprintf(buffer, arg1);
...
}

%eip

text ... 00 00 00 00 %ebp %eip &arg1 … Haxx0r c0d3


buffer

(1) Load our own code into memory


(2) Somehow get %eip to point to it
HIGH-LEVEL IDEA
void func(char *arg1)
{
char buffer[4];
sprintf(buffer, arg1);
...
}

%eip

text ... 00 00 00 00 %ebp %eip &arg1 … Haxx0r c0d3


buffer

(1) Load our own code into memory


(2) Somehow get %eip to point to it
HIGH-LEVEL IDEA
void func(char *arg1)
{
char buffer[4];
sprintf(buffer, arg1);
...
}

%eip

text ... 00 00 00 00 %ebp %eip &arg1 … Haxx0r c0d3


buffer

(1) Load our own code into memory


(2) Somehow get %eip to point to it
THIS IS NONTRIVIAL

• Pulling off this attack requires getting a few things


really right (and some things sorta right)

• Think about what is tricky about the attack


• The key to defending it will be to make the hard parts
really hard
CHALLENGE 1: LOADING CODE INTO MEMORY
• It must be the machine code instructions

(i.e., already compiled and ready to run)

• We have to be careful in how we construct it:


• It can’t contain any all-zero bytes
- Otherwise, sprintf / gets / scanf / … will stop copying
- How could you write assembly to never contain a full zero byte?
• It can’t make use of the loader (we’re injecting)
• It can’t use the stack (we’re going to smash it)
WHAT KIND OF CODE WOULD WE WANT TO RUN?

• Goal: full-purpose shell


• The code to launch a shell is called “shell code”
• It is nontrivial to it in a way that works as injected code
- No zeroes, can’t use the stack, no loader dependence
• There are many out there
- And competitions to see who can write the smallest

• Goal: privilege escalation


• Ideally, they go from guest (or non-user) to root
SHELLCODE
#include <stdio.h>
int main( ) {
char *name[2];
name[0] = “/bin/sh”;
name[1] = NULL;
execve(name[0], name, NULL);
}
SHELLCODE
#include <stdio.h>
int main( ) {
char *name[2];
name[0] = “/bin/sh”;
name[1] = NULL;
execve(name[0], name, NULL);
}

xorl %eax, %eax


pushl %eax
Assembly

pushl $0x68732f2f
pushl $0x6e69622f
movl %esp,%ebx
pushl %eax
...
SHELLCODE
#include <stdio.h>
int main( ) {
char *name[2];
name[0] = “/bin/sh”;
name[1] = NULL;
execve(name[0], name, NULL);
}

xorl %eax, %eax


pushl %eax
Assembly

pushl $0x68732f2f
pushl $0x6e69622f
movl %esp,%ebx
pushl %eax
...
SHELLCODE
#include <stdio.h>
int main( ) {
char *name[2];
name[0] = “/bin/sh”;
name[1] = NULL;
execve(name[0], name, NULL);
}

xorl %eax, %eax “\x31\xc0”

Machine code
pushl %eax “\x50”
Assembly

pushl $0x68732f2f “\x68””//sh”


pushl $0x6e69622f “\x68””/bin”
movl %esp,%ebx “\x89\xe3”
pushl %eax “\x50”
... ...
SHELLCODE
#include <stdio.h>
int main( ) {
char *name[2];
name[0] = “/bin/sh”;
name[1] = NULL;
execve(name[0], name, NULL);
}

xorl %eax, %eax “\x31\xc0”

Machine code
pushl %eax “\x50”
Assembly

pushl $0x68732f2f “\x68””//sh”


(Part of)
pushl $0x6e69622f “\x68””/bin” your
movl %esp,%ebx “\x89\xe3” input
pushl %eax “\x50”
... ...
PRIVILEGE ESCALATION
• More on Unix permissions later, but for now…

• Recall that each file has:


• Permissions: read / write / execute
• For each of: owner / group / everyone else

• Permissions are defined over userid’s and groupid's


• Every user has a userid
• root’s userid is 0

• Consider a service like passwd


• Owned by root (and needs to do root-y things)
• But you want any user to be able to execute it
REAL VS EFFECTIVE USERID
• (Real) Userid = the user who ran the process

• Effective userid = what is used to determine what


permissions/access the process has

• Consider passwd: root owns it, but users can run it


• getuid() will return who ran it (real userid)
• seteuid(0) to set the effective userid to root
- It’s allowed to because root is the owner

• What is the potential attack?


REAL VS EFFECTIVE USERID
• (Real) Userid = the user who ran the process

• Effective userid = what is used to determine what


permissions/access the process has

• Consider passwd: root owns it, but users can run it


• getuid() will return who ran it (real userid)
• seteuid(0) to set the effective userid to root
- It’s allowed to because root is the owner

• What is the potential attack?


If you can get a root-owned process to run

setuid(0)/seteuid(0), then you get root permissions
CHALLENGE 2: GETTING OUR INJECTED CODE TO RUN

• All we can do is write to memory from buffer onward


• With this alone we want to get it to jump to our code
• We have to use whatever code is already running

... 00 00 00 00 %ebp %eip &arg1 …


buffer

Thoughts?
CHALLENGE 2: GETTING OUR INJECTED CODE TO RUN

• All we can do is write to memory from buffer onward


• With this alone we want to get it to jump to our code
• We have to use whatever code is already running

... 00 00 00 00 %ebp %eip &arg1 …


buffer

Thoughts?
CHALLENGE 2: GETTING OUR INJECTED CODE TO RUN

• All we can do is write to memory from buffer onward


• With this alone we want to get it to jump to our code
• We have to use whatever code is already running

... 00 00 00 00 %ebp %eip &arg1 … \x0f \x3c \x2f ...

buffer

Thoughts?
CHALLENGE 2: GETTING OUR INJECTED CODE TO RUN

• All we can do is write to memory from buffer onward


• With this alone we want to get it to jump to our code
• We have to use whatever code is already running

%eip

text ... 00 00 00 00 %ebp %eip &arg1 … \x0f \x3c \x2f ...

buffer

Thoughts?
CHALLENGE 2: GETTING OUR INJECTED CODE TO RUN

• All we can do is write to memory from buffer onward


• With this alone we want to get it to jump to our code
• We have to use whatever code is already running

%eip

text ... 00 00 00 00 %ebp %eip &arg1 … \x0f \x3c \x2f ...

buffer

Thoughts?
CHALLENGE 2: GETTING OUR INJECTED CODE TO RUN

• All we can do is write to memory from buffer onward


• With this alone we want to get it to jump to our code
• We have to use whatever code is already running

%eip

text ... 00 00 00 00 %ebp %eip &arg1 … \x0f \x3c \x2f ...

buffer

Thoughts?
STACK & FUNCTIONS: SUMMARY
Calling function (before calling):
1.Push arguments onto the stack (in reverse)
2.Push the return address, i.e., the address of the instruction you want run after
control returns to you: e.g., %eip + 2
3.Jump to the function’s address

Called function (when called):


4.Push the old frame pointer onto the stack: push %ebp
5.Set frame pointer %ebp to where the end of the stack is right now: %ebp=%esp
6.Push local variables onto the stack; access them as offsets from %ebp

Called function (when returning):


7.Reset the previous stack frame: %esp = %ebp; pop %ebp
8.Jump back to return address: pop %eip

Calling function (after return):


9.Remove the arguments off of the stack: %esp = %esp + number of bytes of args
HIJACKING THE SAVED %EIP

%eip %ebp

text ... 00 00 00 00 %ebp %eip &arg1 … \x0f \x3c \x2f ...

buffer
0xbff
HIJACKING THE SAVED %EIP

%eip %ebp

text ... 00 00 00 00 %ebp %eip


0xbff &arg1 … \x0f \x3c \x2f ...

buffer
0xbff
HIJACKING THE SAVED %EIP

%ebp %eip

text ... 00 00 00 00 %ebp %eip


0xbff &arg1 … \x0f \x3c \x2f ...

buffer
0xbff
HIJACKING THE SAVED %EIP

%ebp %eip

text ... 00 00 00 00 %ebp %eip


0xbff &arg1 … \x0f \x3c \x2f ...

buffer
0xbff

But how do we know the address?


HIJACKING THE SAVED %EIP
What if we are wrong?

%eip %ebp

text ... 00 00 00 00 %ebp %eip


0xbff &arg1 … \x0f \x3c \x2f ...

buffer
0xbff
HIJACKING THE SAVED %EIP
What if we are wrong?

%eip %ebp

text ... 00 00 00 00 %ebp %eip


0xbff &arg1 …
0xbdf \x0f \x3c \x2f ...

buffer
0xbff
HIJACKING THE SAVED %EIP
What if we are wrong?

%ebp %eip

text ... 00 00 00 00 %ebp %eip


0xbff &arg1 …
0xbdf \x0f \x3c \x2f ...

buffer
0xbff
HIJACKING THE SAVED %EIP
What if we are wrong?

%ebp %eip

text ... 00 00 00 00 %ebp %eip


0xbff &arg1 …
0xbdf \x0f \x3c \x2f ...

buffer
0xbff

This is most likely data,


so the CPU will panic
(Invalid Instruction)
CHALLENGE 3: FINDING THE RETURN ADDRESS
CHALLENGE 3: FINDING THE RETURN ADDRESS
• If we don’t have access to the code, we don’t know how
far the buffer is from the saved %ebp
CHALLENGE 3: FINDING THE RETURN ADDRESS
• If we don’t have access to the code, we don’t know how
far the buffer is from the saved %ebp

• One approach: just try a lot of different values!


CHALLENGE 3: FINDING THE RETURN ADDRESS
• If we don’t have access to the code, we don’t know how
far the buffer is from the saved %ebp

• One approach: just try a lot of different values!

• Worst case scenario: it’s a 32 (or 64) bit memory space,


which means 232 (264) possible answers
CHALLENGE 3: FINDING THE RETURN ADDRESS
• If we don’t have access to the code, we don’t know how
far the buffer is from the saved %ebp

• One approach: just try a lot of different values!

• Worst case scenario: it’s a 32 (or 64) bit memory space,


which means 232 (264) possible answers

• But without address randomization:


• The stack always starts from the same, fixed address
• The stack will grow, but usually it doesn’t grow very deeply
(unless the code is heavily recursive)
IMPROVING OUR CHANCES: NOP SLEDS
nop is a single-byte instruction
(just moves to the next instruction)

%eip %ebp

text ... 00 00 00 00 %ebp %eip


0xbff &arg1 …
0xbdf \x0f \x3c \x2f ...

buffer
0xbff
IMPROVING OUR CHANCES: NOP SLEDS
nop is a single-byte instruction
(just moves to the next instruction)

%eip %ebp

text ... 00 00 00 00 %ebp %eip &arg1


0xbff nop
0xbdf nop nop …
… \x0f \x3c \x2f ...

buffer
0xbff
IMPROVING OUR CHANCES: NOP SLEDS
nop is a single-byte instruction
(just moves to the next instruction)

Jumping anywhere
%eip %ebp here will work

text ... 00 00 00 00 %ebp %eip &arg1


0xbff nop
0xbdf nop nop …
… \x0f \x3c \x2f ...

buffer
0xbff
IMPROVING OUR CHANCES: NOP SLEDS
nop is a single-byte instruction
(just moves to the next instruction)

Jumping anywhere
%eip %ebp here will work

text ... 00 00 00 00 %ebp %eip &arg1


0xbff nop
0xbdf nop nop …
… \x0f \x3c \x2f ...

buffer
IMPROVING OUR CHANCES: NOP SLEDS
nop is a single-byte instruction
(just moves to the next instruction)

Jumping anywhere
%eip %ebp here will work

text ... 00 00 00 00 %ebp %eip &arg1


0xbff nop
0xbdf nop nop …
… \x0f \x3c \x2f ...

buffer

Now we improve our chances



of guessing by a factor of #nops
BUFFER OVERFLOWS: PUTTING IT ALL TOGETHER

%eip

text ... 00 00 00 00 %ebp %eip &arg1 …


buffer
BUFFER OVERFLOWS: PUTTING IT ALL TOGETHER

%eip padding

text ... 00 00 00 00 %ebp %eip &arg1 …


buffer
BUFFER OVERFLOWS: PUTTING IT ALL TOGETHER
But it has to be something;

we have to start writing wherever

the input to gets/etc. begins.

%eip padding

text ... 00 00 00 00 %ebp %eip &arg1 …


buffer
BUFFER OVERFLOWS: PUTTING IT ALL TOGETHER
But it has to be something;

we have to start writing wherever

the input to gets/etc. begins.

good

padding
%eip guess

text ... 00 00 00 00 %ebp %eip


0xbdf &arg1 …

buffer
BUFFER OVERFLOWS: PUTTING IT ALL TOGETHER
But it has to be something;

we have to start writing wherever

the input to gets/etc. begins.

good

padding
%eip guess

text ... 00 00 00 00 %ebp %eip &arg1


0xbdf nop nop nop …

buffer

nop sled
BUFFER OVERFLOWS: PUTTING IT ALL TOGETHER
But it has to be something;

we have to start writing wherever

the input to gets/etc. begins.

good

padding
%eip guess

text ... 00 00 00 00 %ebp %eip &arg1


0xbdf nop nop nop …
… \x0f \x3c \x2f ...

buffer

nop sled malicious code


BUFFER OVERFLOWS: PUTTING IT ALL TOGETHER
But it has to be something;

we have to start writing wherever

the input to gets/etc. begins.

good

padding
guess %eip

text ... 00 00 00 00 %ebp %eip &arg1


0xbdf nop nop nop …
… \x0f \x3c \x2f ...

buffer

nop sled malicious code


BUFFER OVERFLOWS: PUTTING IT ALL TOGETHER
But it has to be something;

we have to start writing wherever

the input to gets/etc. begins.

good

padding
guess %eip

text ... 00 00 00 00 %ebp %eip &arg1


0xbdf nop nop nop …
… \x0f \x3c \x2f ...

buffer

nop sled malicious code

You might also like