[드림핵 시스템 해킹] Double Free Bug

asdf·2025년 1월 22일

pwnable

목록 보기
30/36

Double Free Bug


Double Free Bug

Double Free Bug(DFG)는 같은 청크를 두 번 해제할 수 있는 버그를 말합니다. ptmalloc2에서 발생하는 버그 중 하나이며, 공격자에게 임의 주소쓰기, 읽기, 임의 코드 실행 등의 수단으로 활용될 수 있습니다.

Dangling Pointer는 Double free bug를 유발하는 대표적인 원인입니다. Double free bug를 이용하면 duplicated free list를 만드는 것이 가능합니다.

해제된 청크는 fd와 bk로 연결되고 같은 청크를 두 번 해제했다는 뜻은 해제된 청크 A를 재할당해서 데이터를 저장하면, 두 번 해제한 청크 A 중 free list에 남아있는 청크 A의 값이 변한다는 의미이고, 이는 청크 A의 fd와 bk의 값을 변경할 수 있다는 뜻입니다.

초기에는 Double free bug가 있으면 손쉽게 트리거할 수 있었지만, glibc 2.26버전부터 보호 기법이 도입되어 이를 우회하지 않으면 같은 청크를 두 번 해제하는 즉시 프로세스가 종료되게 됩니다.

우회 기법

_int_free은 청크를 해제할 때 호출되는 함수입니다. 하단의 코드의 20번째 줄 이하를 보면, 재할당하려는 청크의 key값이 tcache이면 Double Free가 발생했다고 보고 프로그램을 abort시킵니다.

_int_free (mstate av, mchunkptr p, int have_lock)
 #if USE_TCACHE
    {
     size_t tc_idx = csize2tidx (size);
-
-    if (tcache
-       && tc_idx < mp_.tcache_bins
-       && tcache->counts[tc_idx] < mp_.tcache_count)
+    if (tcache != NULL && tc_idx < mp_.tcache_bins)
       {
-       tcache_put (p, tc_idx);
-       return;
+       /* Check to see if it's already in the tcache.  */
+       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.  */
+         }
+
+       if (tcache->counts[tc_idx] < mp_.tcache_count)
+         {
+           tcache_put (p, tc_idx);
+           return;
+         }
       }
   }
  #endif

즉 if (__glibc_unlikely (e->key == tcache))를 통과하기 위해 key의 1비트만이라도 바꿀 수 있으면 우회할 수 있습니다.

아래 코드는 tcache에 적용된 double free 보호 기법을 우회하는 코드입니다.

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

int main() {
 void *chunk = malloc(0x20);
 printf("Chunk to be double-freed: %p\n", chunk);

 free(chunk);

 *(char *)(chunk + 8) = 0xff;  // key 변경
 free(chunk);                  // 두 번 해제

 printf("First allocation: %p\n", malloc(0x20));
 printf("Second allocation: %p\n", malloc(0x20));

 return 0;
}

실행하면 chunk가 tcache에 중복 연결되어 연속으로 재할당되는 것을 확인할 수 있습니다.

$ ./tcache_dup
Chunk to be double-freed: 0x55d4db927260
First allocation: 0x55d4db927260
Second allocation: 0x55d4db927260
profile
Rainy Waltz(a_hisa)

0개의 댓글