310 likes | 522 Views
Buffer Overflow. Introduction. On many C implementations, it is possible to corrupt the execution stack by writing past the end of an array. Known as smash the stack. It can cause return from the routine to jump to a random address.
E N D
Introduction • On many C implementations, it is possible to corrupt the execution stack by writing past the end of an array. • Known as smash the stack. • It can cause return from the routine to jump to a random address. • Attackers can control the program flow by sending carefully crafted set of input.
Process Memory Organization Lower Memory addresses Text Data Stack Higher Memory addresses Process Memory Regions
Text region • Fixed by the program • Includes code (instructions) • Read only • Data region • Contains initialized and uninitialized data • Static variables are stored here. • Stack region • LIFO
Stack is used to • Dynamically allocate the local variables used in functions. • Pass parameters to the functions. • Return values from the function. • Stack pointer (SP) points to the top of the stack. • The bottom of the stack is at a fixed address. • Consists of logical stack frames that are pushed when calling a function and popped when returning. • Frame pointer (FP) points to a fixed location within a frame.
Let’s see an example • After gcc –S –o example1.s example1.c • Call function is translated to pushl $3 pushl $2 pushl $1 call function
Its pushes the 3 arguments backwards into the stack. • The instruction ‘call’ will push the IP onto the stack. • Procedure prolog pushl %ebp movl %esp, %ebp subl $20, %esp
pushes the FP onto the stack. • Copies the current SP onto EBP, make it the new FP. • Allocates space for the local variables by subtracting their size from SP. • Memory can only be addressed in multiples of the word size. • 5 byte buffer take 8 bytes (2 words). • 10 byte buffer take 12 bytes (3 words). • SP is subtracted by 20
buffer2 buffer1 sfp ret a b c Stack
Buffer Overflows • Result of stuffing more data into a buffer than it can handle. • See the following example: • It copies a string without bounds checking by using strcpy() instead of strncpy(). • Copy the contents of *str into buffer[] until a null character is found.
Buffer[] (16 bytes) is much smaller than *str (256 bytes). • All 250 bytes after buffer in the stack are being overwritten with character ‘A’ (0x41) • Include SFP, RET and even *str. • The return address becomes 0x41414141. • That’s why you get segmentation error when the function returns. buffer sfp ret *str Stack
What is the trick? • Buffer overflow allows us to change the return address of a function. • Add 12 to buffer[]’s address (return address). • Skip pass the assignment to the printf call.
By using a similar technique, a hacker can change the return address, so that the flow control will pass to his code. • The code will be run under the username of the owner of the program. • Usually a shell will be run. • Read the article by Aleph One, to see how the shell code is obtained. • Here is an example to test the shell code.
Writing an Exploit • Suppose we want to “overflow” this program. • Create a program to put the overflow string in an environment variable. • The program takes as parameters a buffer size and an offset from its own stack. • Guess the position of the buffer we want to overflow.
The problem is we need to guess the exactly where the address of our code will start. • A way to increase our chances • Pad the front of our overflow buffer with NOP instructions. • Fill half of the buffer with NOP. • Place the code in the middle. • Then follow it with the return addresses.
If the return address points anywhere in the string of NOPs, they will just get executed until they reach our code. • Further improvement. • Put the code in another place. • The environment variable contains pointer to another environment variable that contains the code. • The environment variables are stored on the stack when the program is started.
If vulnerable is owned by root, we can access the root account. • Trial [mhwong]$ ./exploit4 768 using address: 0xbffff9d8 [mhwong]$ ./vulnerable $RET bash# whoami root bash# Hacker: “Ha! Ha! Ha! …I am the root now!!”
Advanced Buffer Overflow Techniques • Pass through filtering • Programs may filter some characters or converts characters into other characters. • It is much more difficult to write an exploit program, as the normal shell code cannot pass through the filter. • For example, program may convert small letters into capital letters.
Solution • Modify the shell code so that it doesn’t contain small letters. • E.g. \x2f\x62\x69\x6e\x2f\x73\x68 ( /bin/sh) is converted to \x2f\x12\x19\x1e\x2f\x23\x18 • Change the characters back when the shellcode is executed. • Add \0x50 to \x62, \x69, \x6e, and \x68.
Change uid • As programs run with root permission are dangerous, some programs may • Call seteuid(getuid()) at start. • Calls seteuid(0) when needed. #include<string.h> #include<unistd.h> int main(int argc,char **argv) { char buffer[1024]; seteuid(getuid()); if(argc>1) strcpy(buffer,argv[1]); }
One may think that “strcpy(buffer,argv[1])” is OK, as the hacker can only get his own shell. • In fact, the hacker can insert a code which calls setuid(0) in the shellcode to get the root shell. setuid(0); code ---------------------------------------------------------------------------- char code[]= "\x31\xc0" /* xorl %eax,%eax */ "\x31\xdb" /* xorl %ebx,%ebx */ "\xb0\x17" /* movb $0x17,%al */ "\xcd\x80"; /* int $0x80 */ ----------------------------------------------------------------------------
Break chroot • If the vulnerable program is chrooted, you can access only chrooted directory. #include<string.h> #include<unistd.h> int main(int argc,char **argv) { char buffer[1024]; chroot("/home/ftp"); chdir("/"); if(argc>1) strcpy(buffer,argv[1]); }
If you can execute the code below, you can break chroot. • Compile and disassemble the above program and then get the shellcode. main() { mkdir("sh",0755); chroot("sh"); /* many "../" */ chroot("../../../../../../../../../../../../../../../../"); }
Open socket • Overflow the buffer in daemon. • Create a shellcode that do the following steps. • Execute a shell • Open a socket • Connect your standard I/O
Techniques of Avoiding Buffer Overflow • Modern Programming Languages • Most modern programming languages are essentially immune to this problem. • Automatically resize arrays. E.g. Perl, Java • Detect and prevent buffer overflows.E.g. Ada95, Java • C language provides no protection against such problems. • C++ can be easily used in ways to cause this problem.
Careful Use of C/C++ Library Functions • avoid using functions that do not check bounds, unless the bounds will never get exceeded. • Avoid strcpy(3), strcat(3), sprintf(3) and gets(3). • These functions can be replaced with strncpy(3), strncat(3), snprintf(3) and fgets(3). • Strlen(3) should be avoided unless you can guarantee that there will be a terminating NUL character.
Static and dynamically allocated buffer • Fix length buffer may be exploitable. • Attacker sets up a really long string. • When the string is truncated, the final result will be what the attacker wanted. • Dynamically (re-)allocate all strings instead of using fixed-size buffers is recommended. • Must be prepared for dynamic allocation to fail. • Designed to be fail-safe when memory is exhausted.
Use newer libraries. • Newer libraries for C include the strlcpy(3) and strlcat(3). • Take full size of the destination buffer as a parameter. • Guarantee to NUL-terminate the result. • Strlcpy copies up to size-1 characters from the NUL-terminated string src to dst and NUL-terminates the result. • The strlcat function appends the NUL-terminated string src to the end of dst. It will append at most size-strlen(dst) –1 bytes, and NUL-terminates the results.
Compilation solutions in C/C++ • Newer compilers perform bounds-checking. • But it is not wise to depend on this technique as your sole defense. • Only provide partial defense against buffer overflows. • Non-executable user stack area • It is possible to modify the kernel of any OS so that the stack segment is not executable. • However, attackers can simply force the system to call other “interesting” locations already in the program.
Avoid installing set-user-id programs • Program is not running suid root. • User cannot achieve greater access levels. • However, you never know who is going to take your program and set the suid bit on the binary. • It is also possible that users of your software with no privileges at all. That means any successful buffer overflow attack will give them more privileges.
Reference • Aleph One, “Smashing The Stack for Fun and Profit”, Phrack 49 Volume 7, Issue 49, File 14 of 16. • Taeho Oh, “Advanced buffer overflow exploit”.