(1) Bypassing Stackguard and Stackshield http://www.phrack.org/phrack/56/p56-0x05 StackGuard: - put a canary value right below $ra and right above $fp in the stack (where "below" and "above" are relative to the direction in which the stack GROWs, not relative to addresses) |---------------- | argv[1] |---------------- | argv[0] |---------------- | argc |---------------- | $ra |---------------- | canary |---------------- | $fp |---------------- | local vars... | |---------------- what does canary consist of? - make canary 0x00000000 -- so any string writing functions (such as strcpy) will automatically stop writing once they hit a null char -- so cannot *overwrite* canary and keep going - make canary consist of: NULL(0x00), CR (0x0d), LF (0x0a) and EOF (0xff) -- same deal as above: these chars should cause string-copying functions to terminate - make canary some random value, chosen at run time -- so attacker can't know canary til is launching his attack -- so attacker can't successfully predict canary value so attacker can't overwrite canary with correct value so attack will be detected -- cuz recall that attack code is running simultaneously with target code so can't dynamically alter then recompile attack code while it's already executing (and have the new code, containing the random canary, e.g., then run) how is canary used? - before program returns, checks canary is canary intact? Yes: continue No: emit error via syslog and halt (terminate process using only static code so that attacker can't cause problems via the execution of exit(1) e.g.) --> "static data and code" : read-only, the text segment of the program (this is where GOT overwrite sneaks in) anything else I should know about canaries? - later improved on canary idea by XORing the canary with the $ra on function entry to prevent an attacker from overwriting the $ra while bypassing the canary "automatic detection and prevention of buffer overflow attacks" Crispin Cowan, ... http://www.stanford.edu/~stinson/cs155/rdg/stackguard_usenix98.ps (includes good intro to buffer overflow attacks) -- underlying assumption: the $ra is unaltered IFF the canary word is unaltered preventing buff oflow: - detect changes to $ra before function returns - prevent write of $ra why "canary"? - "In the old coal mining days, a canary was placed in a cage and put into mine shafts in order to detect any traces of methane gases that would naturally seep up from deep below the surface of the earth. If a miner saw a canary lying flat dead on its back, then he'd know it was high time to get the hell out a' there!" overcoming StackGuard: (1) skip over the canary word (2) simulte the canary word preventing changes to $ra with MemGuard: - quasi-invariants: state properties taht hold for awhile but may change without notice - use quasi-invariants with code optimizations: while some QIs hold, use X code optimizations - treat return addys on the stack as quasi-invariant -- $ra is read-only while function is active MemGuard: locate code statements that change quasi-invariant values - granualarity is fine: a single word can be marked read-only - protect a $ra when a function is called - start protection and end protection occur where canary placement and canary check occur in StackGuard - implement via marking virtual mem pages containing quasi-invariant terms as read-only; install trap handler to catch writes to protected pages cost of an ordinary write: X cost of a write to a non-protected page: 1800 * X --> not acceptable when the protected words are located next to some frequently written words - requires extension of VM model to protect user (not just kernel) pages - deal with performance penalties due to "false sharing," caused by frequent writes to words near to $ra - app process must trap to kernel mode to protect a word --> need API for this (sys-call interface) - in practice: Pentium has 4 registers which can contain mem addys; if a virtual addy in one of those registers is written to, then an exception can be generated --> so now not paying heavy penalty of having a write- protected virtual page but instead can just keep track of at most 4 $ra's - but "it is only probabilistically true that protecting the four most recent return addresses will capture all protection needs for the top of the stack" - what stackguard doesn't protect against: -- overwriting of function pointers that live on the stack and which are subsequently called by the program - perf costs: -- push canary word onto stack -- check canary word intact before performing fxn return -- MemGuard is very expensive overall: canary version requires 6% more CPU time 7% more real time memguard register: 214% more real time VM version 5100% more real time but "StackGuard protective mechanism is only necessary on privileged admin programs" - convert root vulnerabilities into mild degradation-of-service attacks ----------- StackShield ----------- - create separate stack to store $ra's - at beginning of "a protected function," copy the $ra to a special table - then at beginning of that same function, copy back to $ra from that table - "latest version" : if call a function from a function pointer which points to some area NOT in the .TEXT segment, then halt program execution (2) Exploit that bypassed StackGuard: http://www.securityfocus.com/archive/1/83769 "The exploiting string overwrites exit() GOT entry and makes it point to our shellcode (it's sufficient if the stack is executable)" GOT : Global Offset Table -- executable contains this -- with format string vulnerabilities, recall can write any arbitrary addy -- GOT : has an entry for every library fxn used by the program - e.g. contains addy where fxn is: printf 0x18c3... -- before prog has used that function for the first time, the GOT entry contains the address of the run-time linker (RTL) -- when that fxn is first called, control is passed to the RTL and the fxn's addy is resolved and put into the GOT -- subsequent calls to that fxn will go directly to the addy specified in the GOT -- e.g. where "target1" is a binary/executable elaine15:> objdump --dynamic-reloc target1 target1: file format elf32-little DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 080496a0 UNKNOWN __gmon_start__ 080496a4 UNKNOWN stderr 08049690 UNKNOWN fprintf 08049694 UNKNOWN __libc_start_main 08049698 UNKNOWN exit 0804969c UNKNOWN strcpy -- by overwriting a GOT entry for a function, we can make the next call to that function go where we want it to -- if two systems have the same binary running, the GOT entry will always be the same address(?) -- use format string vulernability to overwrite GOT entry -- stack-based protections will fail (3) // Example vulnerable program. int f (char ** argv) { int pipa; // useless variable char *p; char a[30]; p=a; printf ("p=%x\t -- before 1st strcpy\n",p); strcpy(p,argv[1]); // <== vulnerable strcpy() printf ("p=%x\t -- after 1st strcpy\n",p); strncpy(p,argv[2],16); printf("After second strcpy ;)\n"); } main (int argc, char ** argv) { f(argv); execl("back_to_vul","",0); //<-- The exec that fails printf("End of program\n"); } Okay so the idea is that we have: |---------------| | --------------> argv[2] |---------------| | --------------> argv[1] |---------------| |-->| --------------> argv[0] | |---------------| | | argc | | |---------------| | | main $ra | | |---------------| | | canary | | |---------------| | | main $fp | | |---------------| --------| | |---------------| | f() $ra | |---------------| | canary | |---------------| | f() $fp | |---------------| pipa | | |---------------| p | | |---------------| | | | | | | | | a | | |---------------| --> so during first strcpy() copy $ra into p --> then during second, strncpy( p, argv[2], 16 ) ==> you're writing over the $ra ==> with whatever value you have for argv[2] ==> and you haven't modified the canary! --> but "a" buf is only 30 bytes long plus have to write 4 particular bytes for p and can't write more than 8 bytes after p since there's a canary there... --> so we alter p in the first strcpy() to point to where $ra is stored; so in the second strcpy() we're actually altering the value stored in $ra to point to our shellcode --> so where to put the shell code? at some high addy --> where to have $ra jump to? where the shell code is From the phrack paper detailing this attack: --------------------------------------------------------------------------- So what do we require for our attack? 1. We need pointer p to be physically located on the stack after our buffer a[]. 2. We need an overflow bug that will allow us to overwrite this p pointer (i.e.: an unbounded strcpy). 3. We need one *copy() function (strcpy, memcopy, or whatever) that takes *p as a destination and user-specified data as the source, and no p initialization between the overflow and the copy. ---------------------------------------------------------------------------