Post

[glibc 2.32] ptmalloc2 분석(6)

개요

이전 포스터에 이어서 glibc 2.32 버전에서 패치된 사항을 분석하고자 합니다. safe-linking이 생기면서 많은 부분에서 수정이 이루어져서 포스터를 작성합니다. 앞선 포스터를 먼저 보시는 것을 추천드립니다!

Safe Linking

Safe Linking과 관련하여 추가된 설명은 아래와 같습니다.

1
2
3
4
5
6
7
8
9
/* Safe-Linking:
   Use randomness from ASLR (mmap_base) to protect single-linked lists
   of Fast-Bins and TCache.  That is, mask the "next" pointers of the
   lists' chunks, and also perform allocation alignment checks on them.
   This mechanism reduces the risk of pointer hijacking, as was done with
   Safe-Unlinking in the double-linked lists of Small-Bins.
   It assumes a minimum page size of 4096 bytes (12 bits).  Systems with
   larger pages provide less entropy, although the pointer mangling
   still works.  */

위의 내용을 해석하면 다음과 같습니다.

Safe-Linking은 메모리 할당 및 해제 중에 발생할 수 있는 보안 취약점을 감지하고 방지하는 메커니즘입니다. 이것은 주로 빠른 할당을 위한 Fast-Bins 및 TCache와 같은 단일 연결 리스트의 “next” 포인터를 마스킹하여 구현됩니다. 여기서 ASLR이 활용됩니다. ASLR의 무작위성을 사용하여 메모리 주소의 무작위성을 가져와서 단일 연결 리스트의 다음 포인터를 보호합니다. 이렇게 함으로써 포인터를 악용하여 발생할 수 있는 공격을 방지합니다. ASLR은 메모리 할당에 사용되는 mmap_base로부터 무작위성을 얻어옵니다.

이를 위한 코드로 아래와 같은 매크로 함수가 추가되었습니다.

1
2
3
#define PROTECT_PTR(pos, ptr) \
  ((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
#define REVEAL_PTR(ptr)  PROTECT_PTR (&ptr, ptr)

따라서, fd를 사용하는 부분에 수정이 있었는데 대표적인 예시는 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void *
_int_malloc (mstate av, size_t bytes)
{
	
	*fb = REVEAL_PTR (victim->fd);
	
}

...
	
static void
_int_free (mstate av, mchunkptr p, int have_lock)
{
	p->fd = PROTECT_PTR (&p->fd, old);
	
}

fastbin 검증 추가

_int_malloc

_int_malloc 함수에서 아래와 같이 주소 정렬에 대한 검증이 추가된 것을 확인할 수 있습니다.

1
2
if (__glibc_unlikely (misaligned_chunk (victim)))
  malloc_printerr ("malloc(): unaligned fastbin chunk detected 2");

위와 같이 fastbin에 대해서 해당 chunk의 주소가 제대로 정렬되어있는지 검증하는 코드가 추가되었습니다. 여기서 에러 코드의 숫자가 어디서 에러가 발생하는지 의미하게 됩니다.

  • 숫자가 없는 경우 매크로 함수인 REMOVE_FB에서 발생한 것을 의미합니다.
  • 숫자가 2인경우 fastbin 로직에서 발생한 것을 의미합니다.
  • 숫자가 3인경우 tcache 로직에서 발생한 것을 의미합니다.

_int_free

또한 malloc과 마찬가지로 _int_free 함수에서도 주소 정렬에 대한 검증이 추가되었습니다.

1
2
if (__glibc_unlikely (!aligned_OK (tmp)))
  malloc_printerr ("free(): unaligned chunk detected in tcache 2");

tcache 검증 추가

tcache에서도 마찬가지로 safe linking이 도입되었고 주소 정렬 검증도 도입되었습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* Caller must ensure that we know tc_idx is valid and there's room
   for more chunks.  */
static __always_inline void
tcache_put (mchunkptr chunk, size_t tc_idx)
{
  tcache_entry *e = (tcache_entry *) chunk2mem (chunk);

  /* Mark this chunk as "in the tcache" so the test in _int_free will
     detect a double free.  */
  e->key = tcache;

  e->next = PROTECT_PTR (&e->next, tcache->entries[tc_idx]);
  tcache->entries[tc_idx] = e;
  ++(tcache->counts[tc_idx]);
}

/* Caller must ensure that we know tc_idx is valid and there's
   available chunks to remove.  */
static __always_inline void *
tcache_get (size_t tc_idx)
{
  tcache_entry *e = tcache->entries[tc_idx];
  if (__glibc_unlikely (!aligned_OK (e)))
    malloc_printerr ("malloc(): unaligned tcache chunk detected");
  tcache->entries[tc_idx] = REVEAL_PTR (e->next);
  --(tcache->counts[tc_idx]);
  e->key = NULL;
  return (void *) e;
}

결론

glibc 2.32에서 많은 패치는 이루어지지 않았지만 중요한 개념인 safe linking과 검증을 위한 로직이 추가된 것을 확인할 수 있습니다.

💡 다만, 해당 보호기법은 해제된 chunk의 데이터를 leak할 수 있다면 우회가 가능한 보호기법입니다.

This post is licensed under CC BY 4.0 by the author.