GNU system과 GNU/Linux 시스템의 핵심 라이브러리를 제공하는 GNU C 라이브러리
tcache_entry 구조체
// GLIBC 2.29
typedef struct tcache_entry
{
struct tcache_entry *next;
/* This field exists to detect double frees. */
struct tcache_perthread_struct *key;
} tcache_entry;
GLIBC 2.26과 다르게, GLIBC 2.29에서는 tcache_put, tcache_get 함수에 Double Free 취약점을 검증하는 코드가 추가되었다.
GLIBC 2.26에서는 tcache_entry 구조체에 tcache_entry *next만 있었다면, GLIBC 2.29에서는 tcache_perthread_struct *key 멤버가 추가되었다.
tcache_put 함수
static __always_inline void tcache_put (mchunkptr chunk, size_t tc_idx)
{
tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
assert (tc_idx < TCACHE_MAX_BINS);
/* Mark this chunk as "in the tcache" so the test in _int_free will
detect a double free. */
e->key = tcache;
e->next = tcache->entries[tc_idx];
tcache->entries[tc_idx] = e;
++(tcache->counts[tc_idx]);
}
e->key에 tcache_entry 구조체의 주소, 즉 tcache 포인터를 적어놓는다.
이후 free() 함수 호출로 _init_free() 함수가 호출되면, _init_free() 함수 내부에서 e->key를 Double Free 여부를 확인하는데 사용한다.
tcache_get 함수
static __always_inline void * tcache_get (size_t tc_idx)
{
tcache_entry *e = tcache->entries[tc_idx];
assert (tc_idx < TCACHE_MAX_BINS);
assert (tcache->entries[tc_idx] > 0);
tcache->entries[tc_idx] = e->next;
--(tcache->counts[tc_idx]);
e->key = NULL;
return (void *) e;
}
e->key에 NULL을 넣음
_init_free 함수
tcache_entry *e = (tcache_entry *) chunk2mem (p);
/* This test succeeds on double free. However, we don't 100%
trust it (it also matches random payload data at a 1 in
2^<size_t> chance), so verify it's not an unlikely
coincidence before aborting. */
if (__glibc_unlikely (e->key == tcache))
{
tcache_entry *tmp;
LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
for (tmp = tcache->entries[tc_idx]; tmp; tmp = tmp->next)
if (tmp == e)
malloc_printerr ("free(): double free detected in tcache 2");
/* If we get here, it was a coincidence. We've wasted a
few cycles, but don't abort. */
}
free 함수가 호출되어 힙 청크 해제를 요청하면 __init_free 함수가 호출된다. tcache_put 함수에서는 e->key에 해당 chunk 주소를 넣는다. 만약 e->key에 있는 주소에 대한 해제가 요청되는 경우 double free이므로 에러 출력됨
unsorted bin attack
unsorted bin의 bk를 임의의 주소로 조작하여 해당 영역에 main_arena 주소를 적는 공격. 이를 통해 libc 주소를 leak할 수 있음.
if (__glibc_unlikely (bck->fd != victim)
|| __glibc_unlikely (victim->fd != unsorted_chunks (av)))
malloc_printerr ("malloc(): unsorted double linked list corrupted");
GLIBC 2.29에서의 _int_malloc 함수에서 위 검사로 인하여 unsorted bin 공격을 할 수 없다.
if (__glibc_unlikely (size <= 2 * SIZE_SZ) || __glibc_unlikely (size > av->system_mem))
malloc_printerr ("malloc(): invalid size (unsorted)");
unsorted bin 청크의 size가 할당한 것보다 작거나 할당 가능한 값보다 큰 경우 오류 메세지 출력하고 비정상 종료한다.
victim = av->top;
size = chunksize (victim);
if (__glibc_unlikely (size > av->system_mem))
malloc_printerr ("malloc(): corrupted top size");
GLIBC 2.29 버전부터 top 청크 검증이 추가되어 House of Force 공격(top chunk size를 -1로 할당하고 청크를 임의의 영역에 할당하는 공격)이 불가능해졌다.
_IO_FILE (0) | 2021.01.01 |
---|---|
tcache memory leak (0) | 2020.06.13 |
House of Force (0) | 2020.06.12 |
Unsafe Unlink (0) | 2020.06.12 |
Poison NULL Byte (0) | 2020.06.12 |