Stack Overflows4bis
Stack Overflows4bis
Stack Overflows4bis
1
Buffers
• A buffer is defined as a limited, contiguously
allocated set of memory
• Stack overflows are possible because no
inherent bounds-checking exists onbuffers in
the C or C++ languages
2
reading past the end of a buffer
#include <stdio.h>
#include <string.h>
int main ()
{
int array[5] = {1, 2, 3, 4, 5};
printf(“%d\n”, array[5] );
}
This example shows how easy it is to read past the end of a buffer;
C provides no built-in protection
3
writing past the end of a buffer
int main ()
{
int array[5];
int i;
for (i = 0; i <= 255; i++ )
{
array[i] = 10;
}
}
compiler gives no warnings or errors. But, when we execute this program, it crashes:
4
The Stack
• the stack is a LIFO data structure.
push 1
push addr var
5
pop eax
pop ebx
6
Stacks and Functions
• For each function call, there's a section of the stack reserved for the
function. This is usually called a stack frame
• A stack frame exists whenever a function has started, but yet to complete
main() in a C program
7
• If inside of body of main() there's a call to foo().
• Suppose foo() takes two arguments.
• One way to pass the arguments to foo() is through the
stack.
• Thus, there needs to be assembly language code
in main() to "push" arguments for foo() onto the the
stack.
8
• by placing the arguments on the stack, the stack frame for main() has increased in
size.
• When the arguments are placed onto the stack, the function is called,
placing the return address, or RET value, onto the stack. RET value is the address
stored in the instruction pointer (EIP) at the time function is called.
9
• Once we get into code for foo(), the
function foo() may need local variables,
so foo() needs to push some space on the stack
10
• The frame pointer points to the location where the stack pointer was, just
before foo() moved the stack pointer for foo()'s own local variables.
• Having a frame pointer is convenient when a function is likely to move the stack
pointer several times throughout the course of running the function. The idea is to
keep the frame pointer fixed for the duration of foo()'s stack frame. The stack
pointer, in the meanwhile, can change values.
• Thus, we can use the frame pointer to compute the locations in memory for both
arguments as well as local variables. Since it doesn't move, the computations for
those locations should be some fixed offset from the frame pointer.
11
Example
void function(int a, int b)
{
int array[5];
}
main()
{
function(1,2);
printf(“This is where the return address points”);
}
12
• Before any function instructions can be
executed, the prolog is executed.
• The prolog stores some values onto the stack
so that the function can execute cleanly.
• The current value of EBP is pushed onto the
stack, because the value of EBP must be
changed in order to reference values on the
stack
13
• Once EBP is stored on the stack, we are free to copy the current stack
pointer (ESP) into EBP. Now we can easily reference addresses local to
the stack.
• The last thing the prolog does is to calculate the address space required
for the variables local to functionand reserve this space on the stack.
Subtracting the size of the variables from ESP reserves the required
space.
• Finally, the variables local to function, in this case simply array, are
pushed onto the stack.
14
Visual representation of the stack after a function has been called
15
(gdb) disas main
Dump of assembler code for function main:
0x0804838c <main+0>: push %ebp
0x0804838d <main+1>: mov %esp,%ebp
0x0804838f <main+3>: sub $0x8,%esp
0x08048392 <main+6>: movl $0x2,0x4(%esp)
0x0804839a <main+14>: movl $0x1,(%esp)
0x080483a1 <main+21>: call 0x8048384 <function>
0x080483a6 <main+26>: movl $0x8048500,(%esp)
0x080483ad <main+33>: call 0x80482b0 <_init+56>
0x080483b2 <main+38>: leave
0x080483b3 <main+39>: ret
End of assembler dump.
17
$ cc -mpreferred-stack-boundary=2 –ggdb overflow.c -o overflow
$ ./overflow
18
Phân tích dùng GDB
$ gdb ./overflow
Khảo sát hàm return_input()
21
• Tiếp tục chạy chương trình với nhập vào 40 ký tự
(gdb) continue
Continuing.
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD
23
Kiểm soát EIP
• Thay vì làm tràn bằng DDDD, sẽ làm tràn bằng
địa chỉ nào đó có chủ đích.
• Địa chỉ trả về đọc ra từ stack được nạp vào EIP,
chỉ thị tại đó được thực thi. Đây là cách kiểm
soát thực thi.
24
Ví dụ
• Trước hết cần xác định địa chỉ
• Ví dụ thay địa chỉ của hàm return_input() cho địa
chỉ trả về hàm main().
$ gdb ./overflow
(gdb) disas main
Dump of assembler code for function main:
0x080483ea <main+0>: push %ebp
0x080483eb <main+1>: mov %esp,%ebp
0x080483ed <main+3>: call 0x80483c4 <return_input>
0x080483f2 <main+8>: mov $0x0,%eax
0x080483f7 <main+13>: pop %ebp
0x080483f8 <main+14>: ret
End of assembler dump.
25
Dùng hàm printf của bash shell để thử
AAAAAAAAAABBBBBBBBBBCCCCCCCCCC
$ printf
“AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDD\xed\x83\x04\x08” | ./overflow
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDí
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDò
26