02 Buffer PDF
02 Buffer PDF
02 Buffer PDF
0
ALL PROGRAMS ARE STORED IN MEMORY
4G 0xffffffff
0 0x00000000
ALL PROGRAMS ARE STORED IN MEMORY
4G 0xffffffff
0 0x00000000
ALL PROGRAMS ARE STORED IN MEMORY
4G 0xffffffff
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
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
0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff
cmdline & env
0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff
Set when
cmdline & env
process starts
0 0x00000000
DATA’S LOCATION DEPENDS ON HOW IT’S CREATED
4G 0xffffffff
Set when
cmdline & env
process starts int f() {
Stack int x;
…
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));
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));
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));
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
0x00000000 0xffffffff
Heap Stack
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions
0x00000000 0xffffffff
Heap Stack
Stack
pointer
WE ARE GOING TO FOCUS ON RUNTIME ATTACKS
Stack and heap grow in opposite directions
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
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
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
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
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
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
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
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
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
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
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
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
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 …
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
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
0x00000000 0xffffffff
NOTATION
0xbfff03b8 %ebp A memory address
0x00000000 0xffffffff
NOTATION
0xbfff03b8 %ebp A memory address
0xbfff03b8
0x00000000 0xffffffff
%ebp
NOTATION
0xbfff03b8 %ebp A memory address
0xbfff03b8
0xbfff0720
0x00000000 0xffffffff
%ebp
NOTATION
0xbfff03b8 %ebp A memory address
0xbfff03b8
0xbfff0720
0x00000000 0xffffffff
%ebp
NOTATION
0xbfff03b8 %ebp A memory address
%esp
0xbfff03b8
0xbfff0720
0x00000000 0xffffffff
%ebp
NOTATION
0xbfff03b8 %ebp A memory address
%esp
0xbfff03b8
0xbfff0720
0x00000000 0xffffffff
%ebp
NOTATION
0xbfff03b8 %ebp A memory address
%esp
0xbfff03b8
0xbfff0720
0x00000000 0xffffffff
%ebp
NOTATION
0xbfff03b8 %ebp A memory address
%esp
0xbfff03b8
0xbfff03b8 0xbfff0720
0x00000000 0xffffffff
%ebp
NOTATION
0xbfff03b8 %ebp A memory address
%esp
0xbfff03b8
0xbfff03b8 0xbfff0720
0x00000000 0xffffffff
%ebp
NOTATION
0xbfff03b8 %ebp A memory address
%esp
0xbfff03b8
0xbfff03b8 0xbfff0720
0x00000000 0xffffffff
%ebp
NOTATION
0xbfff03b8 %ebp A memory address
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 */
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
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?
Stack frame
for this call to func %ebp
RETURNING FROM FUNCTIONS
int main()
{
...
func(“Hey”, 10, -3);
...
} Q: How do we restore %ebp?
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?
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?
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
0x00000000 0xffffffff
… loc2 loc1 %ebp %eip
??? arg1 arg2 arg3 caller’s data
Stack frame
%ebp for this call to func %ebp
0x00000000 0xffffffff
… loc2 loc1 %ebp %eip
??? arg1 arg2 arg3 caller’s data
Stack frame
%ebp for this call to func %ebp
Caller’s code
Current stack frame Caller’s stack frame
Caller’s code
Current stack frame Caller’s stack frame
%eip %ebp
%esp
RETURNING FROM A FUNCTION
In C In compiled assembly
leave: mov %esp %ebp
return; pop %ebp
ret: pop %eip
• Overflow =
• Put more into the buffer than it can hold
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);
...
}
int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}
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);
...
}
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);
...
}
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);
...
}
int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}
int main()
{
char *mystr = “AuthMe!”;
func(mystr);
...
}
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);
...
}
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
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’)
int main()
{
func(“Hello”, 10, -3);
return 0;
}
FIRST A RECAP: ARGS
#include <stdio.h>
int main()
{
func(“Hello”, 10, -3);
return 0;
}
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;
}
%eip %ebp
~0x0
0x0
%eip %ebp
~0x0
0x0
%eip %ebp
~0x0
0x0
%eip %ebp
~0x0
0x0
%eip %ebp
~0x0
0x0
%eip %ebp
~0x0
0x0
%eip %ebp
~0x0
0x0
%eip %ebp
~0x0
0x0
%eip %ebp
~0x0
0x0
%eip %ebp
~0x0
0x0
%eip %ebp
~0x0
0x0
%eip %ebp
~0x0
0x0
%eip %ebp
~0x0
0x0
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
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
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
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);
...
}
%eip
%eip
%eip
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);
}
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);
}
Machine code
pushl %eax “\x50”
Assembly
Machine code
pushl %eax “\x50”
Assembly
Thoughts?
CHALLENGE 2: GETTING OUR INJECTED CODE TO RUN
Thoughts?
CHALLENGE 2: GETTING OUR INJECTED CODE TO RUN
buffer
Thoughts?
CHALLENGE 2: GETTING OUR INJECTED CODE TO RUN
%eip
buffer
Thoughts?
CHALLENGE 2: GETTING OUR INJECTED CODE TO RUN
%eip
buffer
Thoughts?
CHALLENGE 2: GETTING OUR INJECTED CODE TO RUN
%eip
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
%eip %ebp
buffer
0xbff
HIJACKING THE SAVED %EIP
%eip %ebp
buffer
0xbff
HIJACKING THE SAVED %EIP
%ebp %eip
buffer
0xbff
HIJACKING THE SAVED %EIP
%ebp %eip
buffer
0xbff
%eip %ebp
buffer
0xbff
HIJACKING THE SAVED %EIP
What if we are wrong?
%eip %ebp
buffer
0xbff
HIJACKING THE SAVED %EIP
What if we are wrong?
%ebp %eip
buffer
0xbff
HIJACKING THE SAVED %EIP
What if we are wrong?
%ebp %eip
buffer
0xbff
%eip %ebp
buffer
0xbff
IMPROVING OUR CHANCES: NOP SLEDS
nop is a single-byte instruction
(just moves to the next instruction)
%eip %ebp
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
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
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
buffer
%eip
%eip padding
%eip padding
good
padding
%eip guess
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
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
buffer
good
padding
guess %eip
buffer
good
padding
guess %eip
buffer