stack buffer overflows
play

Stack Buffer Overflows Deian Stefan Some slides adopted from Kirill - PowerPoint PPT Presentation

CSE 127: Computer Security Stack Buffer Overflows Deian Stefan Some slides adopted from Kirill Levchenko and Stefan Savage When is a program secure? When it does exactly what it should? Not more Not less But how do we know what


  1. 0xffffd0d8 $99 $88 $77 0x08049bbc 0xffffd0d8 %ebp $79 %esp

  2. 0xffffd0d8 $99 $88 $77 0x08049bbc 0xffffd0d8 %ebp $79 %esp

  3. 0xffffd0d8 $99 $88 $77 0x08049bbc 0xffffd0d8 %ebp $79 $91 %esp

  4. 0xffffd0d8 $99 $88 $77 0x08049bbc 0xffffd0d8 %ebp $79 $91 %esp

  5. 0xffffd0d8 $99 $88 $77 0x08049bbc 0xffffd0d8 %ebp $79 $91 $103 %esp

  6. 0xffffd0d8 $99 $88 $77 0x08049bbc 0xffffd0d8 %ebp $79 $91 $103 %esp

  7. 0xffffd0d8 $99 $88 $77 0x08049bbc 0xffffd0d8 %ebp $79 $91 $103 $293 %esp

  8. 0xffffd0d8 $99 $88 $77 0x08049bbc 0xffffd0d8 %ebp $79 $91 $103 $293 %esp

  9. 0xffffd0d8 $99 $88 $77 0x08049bbc 0xffffd0d8 %esp, %ebp $79 $91 $103 $293

  10. %ebp 0xffffd0d8 $99 $88 $77 0x08049bbc 0xffffd0d8 %esp $79 $91 $103 $293

  11. %esp, %ebp 0xffffd0d8 $99 $88 $77 0x08049bbc 0xffffd0d8 $79 $91 $103 $293 %eip = 0x08049bbc

  12. Example 1 argv argc #include <stdio.h> #include <string.h> saved ret int main( int argc, char **argv) { saved ebp char nice[] = "is nice."; %ebp char name[8]; nice[4-7] gets(name); printf("%s %s\n",name,nice); nice[0-3] return 0; name[4-7] } name[0-3] %esp

  13. Example 1 argv argc #include <stdio.h> #include <string.h> saved ret int main( int argc, char **argv) { saved ebp char nice[] = "is nice."; %ebp char name[8]; nice[4-7] gets(name); printf("%s %s\n",name,nice); nice[0-3] return 0; name[4-7] } name[0-3] %esp

  14. Example 1 argv argc #include <stdio.h> #include <string.h> saved ret int main( int argc, char **argv) { saved ebp char nice[] = "is nice."; %ebp char name[8]; nice[4-7] gets(name); printf("%s %s\n",name,nice); nice[0-3] return 0; name[4-7] } name[0-3] %esp

  15. Example 1 argv argc #include <stdio.h> #include <string.h> saved ret int main( int argc, char **argv) { saved ebp char nice[] = "is nice."; %ebp char name[8]; nice[4-7] gets(name); printf("%s %s\n",name,nice); nice[0-3] return 0; name[4-7] } name[0-3] %esp

  16. Example 1 argv argc #include <stdio.h> #include <string.h> saved ret int main( int argc, char **argv) { saved ebp char nice[] = "is nice."; %ebp char name[8]; nice[4-7] gets(name); printf("%s %s\n",name,nice); nice[0-3] return 0; name[4-7] } name[0-3] %esp What happens if we read a long name?

  17. Example 1 argv argc #include <stdio.h> #include <string.h> saved ret int main( int argc, char **argv) { saved ebp char nice[] = "is nice."; %ebp char name[8]; nice[4-7] gets(name); printf("%s %s\n",name,nice); nice[0-3] return 0; name[4-7] } name[0-3] %esp What happens if we read a long name?

  18. Example 1 argv argc #include <stdio.h> #include <string.h> saved ret int main( int argc, char **argv) { saved ebp char nice[] = "is nice."; %ebp char name[8]; nice[4-7] gets(name); printf("%s %s\n",name,nice); nice[0-3] return 0; name[4-7] } name[0-3] %esp If not null terminated can read more of the stack

  19. Let's run this program!

  20. Example 2 #include <stdio.h> #include <string.h> void foo() { printf("hello all!!\n"); exit(0); } void func( int a, int b, char *str) { int c = 0xdeadbeef; char buf[4]; strcpy(buf,str); } int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; }

  21. Example 2 #include <stdio.h> #include <string.h> void foo() { printf("hello all!!\n"); exit(0); } void func( int a, int b, char *str) { int c = 0xdeadbeef; char buf[4]; strcpy(buf,str); } int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; }

  22. Example 2 #include <stdio.h> argv[1] #include <string.h> 0xbbbbbbbb void foo() { printf("hello all!!\n"); 0xaaaaaaaa exit(0); } saved ret void func( int a, int b, char *str) { saved ebp int c = 0xdeadbeef; 0xdeadbeef char buf[4]; strcpy(buf,str); buf[0-3] } int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; }

  23. Example 2 #include <stdio.h> argv[1] #include <string.h> 0xbbbbbbbb void foo() { printf("hello all!!\n"); 0xaaaaaaaa exit(0); } saved ret void func( int a, int b, char *str) { saved ebp int c = 0xdeadbeef; %ebp 0xdeadbeef char buf[4]; strcpy(buf,str); buf[0-3] } int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; }

  24. Example 2 #include <stdio.h> argv[1] #include <string.h> 0xbbbbbbbb void foo() { printf("hello all!!\n"); 0xaaaaaaaa exit(0); } saved ret void func( int a, int b, char *str) { saved ebp int c = 0xdeadbeef; %ebp 0xdeadbeef char buf[4]; strcpy(buf,str); buf[0-3] } %esp int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; }

  25. Example 2 #include <stdio.h> argv[1] #include <string.h> 0xbbbbbbbb void foo() { printf("hello all!!\n"); 0xaaaaaaaa exit(0); } saved ret void func( int a, int b, char *str) { saved ebp int c = 0xdeadbeef; %ebp 0xdeadbeef char buf[4]; strcpy(buf,str); buf[0-3] } %esp int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; }

  26. Example 2 #include <stdio.h> 0x41414141 #include <string.h> 0x41414141 void foo() { printf("hello all!!\n"); 0x41414141 exit(0); } 0x41414141 void func( int a, int b, char *str) { 0x41414141 int c = 0xdeadbeef; %ebp 0x41414141 char buf[4]; strcpy(buf,str); 0x41414141 } %esp int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; } If first argument to program is “AAAAAAAA…”

  27. Example 2 #include <stdio.h> 0x41414141 #include <string.h> 0x41414141 void foo() { printf("hello all!!\n"); 0x41414141 exit(0); } 0x41414141 void func( int a, int b, char *str) { 0x41414141 int c = 0xdeadbeef; %ebp 0x41414141 char buf[4]; strcpy(buf,str); 0x41414141 } %esp int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; }

  28. Example 2 #include <stdio.h> 0x41414141 #include <string.h> 0x41414141 void foo() { printf("hello all!!\n"); 0x41414141 exit(0); } 0x41414141 void func( int a, int b, char *str) { 0x41414141 int c = 0xdeadbeef; %esp, %ebp 0x41414141 char buf[4]; strcpy(buf,str); 0x41414141 } int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; }

  29. Example 2 #include <stdio.h> 0x41414141 #include <string.h> 0x41414141 void foo() { printf("hello all!!\n"); 0x41414141 exit(0); } 0x41414141 void func( int a, int b, char *str) { 0x41414141 int c = 0xdeadbeef; %esp 0x41414141 char buf[4]; strcpy(buf,str); 0x41414141 } int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; } %ebp = 0x41414141

  30. Example 2 #include <stdio.h> 0x41414141 #include <string.h> 0x41414141 void foo() { printf("hello all!!\n"); 0x41414141 exit(0); } 0x41414141 void func( int a, int b, char *str) { 0x41414141 int c = 0xdeadbeef; %esp 0x41414141 char buf[4]; strcpy(buf,str); 0x41414141 } int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; } %ebp = 0x41414141 %eip = 0x41414141

  31. Example 2 #include <stdio.h> 0x41414141 #include <string.h> 0x41414141 void foo() { printf("hello all!!\n"); 0x41414141 exit(0); } 0x41414141 void func( int a, int b, char *str) { 0x41414141 int c = 0xdeadbeef; %esp 0x41414141 char buf[4]; strcpy(buf,str); 0x41414141 } int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; } %ebp = 0x41414141 %eip = 0x41414141

  32. Stack Buffer Overflow • If source string of strcpy controlled by attacker (and destination is on the stack) ➤ Attacker gets to control where the function returns by overwriting the return address ➤ Attacker gets to transfer control to anywhere! • Where do you jump?

  33. Existing functions #include <stdio.h> 0x41414141 #include <string.h> 0x41414141 void foo() { printf("hello all!!\n"); 0x41414141 exit(0); } 0x08049b95 void func( int a, int b, char *str) { 0x41414141 int c = 0xdeadbeef; %ebp 0x41414141 char buf[4]; strcpy(buf,str); 0x41414141 } %esp int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; }

  34. Existing functions #include <stdio.h> 0x41414141 #include <string.h> 0x41414141 void foo() { printf("hello all!!\n"); 0x41414141 exit(0); } 0x08049b95 void func( int a, int b, char *str) { 0x41414141 int c = 0xdeadbeef; %esp, %ebp 0x41414141 char buf[4]; strcpy(buf,str); 0x41414141 } int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; }

  35. Existing functions #include <stdio.h> 0x41414141 #include <string.h> 0x41414141 void foo() { printf("hello all!!\n"); 0x41414141 exit(0); } 0x08049b95 void func( int a, int b, char *str) { 0x41414141 int c = 0xdeadbeef; %esp 0x41414141 char buf[4]; strcpy(buf,str); 0x41414141 } int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; } %ebp = 0x41414141

  36. Existing functions #include <stdio.h> 0x41414141 #include <string.h> 0x41414141 void foo() { printf("hello all!!\n"); 0x41414141 exit(0); } 0x08049b95 void func( int a, int b, char *str) { 0x41414141 int c = 0xdeadbeef; %esp 0x41414141 char buf[4]; strcpy(buf,str); 0x41414141 } int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; } %ebp = 0x41414141 %eip = 0x08049b95

  37. Existing functions #include <stdio.h> 0x41414141 #include <string.h> 0x41414141 void foo() { printf("hello all!!\n"); 0x41414141 exit(0); } 0x08049b95 void func( int a, int b, char *str) { 0x41414141 int c = 0xdeadbeef; %esp 0x41414141 char buf[4]; strcpy(buf,str); 0x41414141 } int main( int argc, char **argv) { func(0xaaaaaaaa,0xbbbbbbbb,argv[1]); return 0; } %ebp = 0x41414141 %eip = 0x08049b95

  38. Let’s look at this in GDB (w/ GEF)

  39. Better Hijacking Control argv[1] 0xbbbbbbbb • Jump to attacker-supplied code 0xaaaaaaaa • Where? We have control of saved ret saved ebp string! %ebp 0xdeadbeef ➤ Put code in string buf[0-3] %esp ➤ Jump to start of string

  40. Better Hijacking Control shellcode • Jump to attacker-supplied code • Where? We have control of hijacked ret string! %ebp ➤ Put code in string %esp ➤ Jump to start of string

  41. Shellcode • Shellcode: small code fragment that receives initial control in an control flow hijack exploit ➤ Control flow hijack: taking control of instruction ptr • Earliest attacks used shellcode to exec a shell ➤ Target a setuid root program, gives you root shell

  42. Shellcode void main() { char *name[2]; name[0] = "/bin/sh"; name[1] = NULL; execve(name[0], name, NULL); }

  43. Shellcode • Can we just take output from gcc/clang?

  44. 
 
 
 Shellcode • There are some restrictions ➤ 1. Shellcode cannot contain null characters ‘\0’ ➤ Why not? ➤ Fix: use different instructions and NOPs to eliminate \0 ➤ 2. If payload is via gets() must also avoid line-breaks 


  45. How do we make this robust? shellcode • 3. Exact address of shellcode &shellcode[0] start not always easy to guess %ebp ➤ Miss? SEGFAULT! • Fix: NOP-sled %esp

  46. How do we make this robust? shellcode • 3. Exact address of shellcode ~&shellcode[0] start not always easy to guess %ebp ➤ Miss? SEGFAULT! • Fix: NOP-sled %esp

  47. How do we make this robust? shellcode NOP-sled • 3. Exact address of shellcode ~&shellcode[0] start not always easy to guess %ebp ➤ Miss? SEGFAULT! • Fix: NOP-sled %esp

  48. Metasploit helps!

  49. Buffer Overflow Defenses • Avoid unsafe functions • Stack canary • Separate control stack • Address Space Layout Randomization (ASLR) • Memory writable or executable, not both (W^X) • Control flow integrity (CFI)

  50. Avoiding Unsafe Functions • strcpy, strcat, gets, etc. • Plus: Good idea in general • Minus: Requires manual code rewrite • Minus: Non-library functions may be vulnerable ➤ E.g. user creates their own strcpy • Minus: No guarantee you found everything • Minus: alternatives are also error-prone

  51. Even printf is tricky If buf is under control of attacker 
 is: printf(“%s\n”, buf) safe?

Recommend


More recommend