[Linux Kernel] kernel heap hardening

dandb3·2024년 6월 27일
0

linux kernel

목록 보기
6/21

SLUB에서의 heap hardening 기법 중 freelist hardening 에 대해 알아볼 것이다.

SLUB의 경우, singly linked list (이면서 FIFO) 구조를 가지는 freelist를 가지고 있다.

linux kernel의 경우에도 freelist의 next 포인터의 난독화 기법을 적용하는데, 소스코드를 보면 아래와 같다.

static inline freeptr_t freelist_ptr_encode(const struct kmem_cache *s,
					    void *ptr, unsigned long ptr_addr)
{
	unsigned long encoded;

#ifdef CONFIG_SLAB_FREELIST_HARDENED
	encoded = (unsigned long)ptr ^ s->random ^ swab(ptr_addr);
#else
	encoded = (unsigned long)ptr;
#endif
	return (freeptr_t){.v = encoded};
}

여기서 swab() 매크로는 endian 변환을 위한 매크로에 해당한다.

즉, 원하는 ptr을 얻기 위해서는 encoded ^ s->random ^ swab(ptr_addr)을 해야 한다.

bypass

freelist의 마지막 원소에 주목해보자.

마지막 원소의 nextNULL에 해당한다.
즉, encoded된 값은 NULL ^ s->random ^ swab(ptr_addr)이다.

만약 UAF를 이용해 각 slot의 next pointer 값들을 읽어올 수 있는 상황을 고려해 보자.
freelist에서 마지막 직전의 slot을 1, 마지막 slot을 2라고 해 보자.

그러면
slot1->next = address of slot2 ^ random ^ swab(address of slot1 + next offset) 이고,
slot2->next = NULL ^ random ^ swab(address of slot2 + next offset)이 된다.

여기서 slot1->next ^ slot2->next를 하게 된다면 결과는
address of slot2 ^ swab(address of slot1 + next offset) ^ swab(address of slot2 + next offset)이 된다.

만약 slot1과 slot2은 인접한 메모리에 있을 것이고, 예를 들어 같은 페이지에 있다고 하면 xor 후 하위 12비트를 제외한 나머지는 0일 것이다.
이를 통해 slot의 주소를 leak할 수 있게 된다.

추가

static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp)
{
	unsigned long freeptr_addr = (unsigned long)object + s->offset;

#ifdef CONFIG_SLAB_FREELIST_HARDENED
	BUG_ON(object == fp); /* naive detection of double free or corruption */
#endif

	freeptr_addr = (unsigned long)kasan_reset_tag((void *)freeptr_addr);
	*(freeptr_t *)freeptr_addr = freelist_ptr_encode(s, fp, freeptr_addr);
}

exploit 코드를 작성하면서 자꾸 double-free 시에 커널 패닉이 일어나길래 찾아보니 CONFIG_SLAB_FREELIST_HARDENED가 설정된 경우에는 바로 연속해서 double-free가 일어날 경우 에러를 탐지하는 코드가 있는 것을 알게 되었다..

profile
공부 내용 저장소

0개의 댓글

관련 채용 정보