Tcache dup

2025. 5. 20. 19:04· Hacking/Pwnable
목차
  1. tcache duplicate
  2. 1. tcache?
  3. 2. tcache 코드 분석 (glibc 2.27)
  4. 3. 동적 분석
  5. 4. tcache dup

tcache duplicate


1. tcache?

1. tcache

glibc 2.26 부터 tcache 가 도입되면서 0x20 ~ 0x410의 크기의 청크는 main arena가 아닌 tcache에서 관리되기 시작했다

tcache는 크기별로 최대 7개까지 저장할 수 있고 가득 찬 이후에는 청크가 fastbin이나 다른 bin으로 이동하게 된다

tcache는 보안 검사가 많이 생략되어 있어 공격자들에게 힙 익스플로잇 도구로 사용된다

 

glibc 2.27 의 tcache 는 double free에 대한 검증 로직이 없는 것으로 알려져 있지만

실습 환경인 glibc 2.27 의 버전은 Ubuntu GLIBC 2.27-3ubuntu1.6 으로 이후 버전에서 도입된 검증 로직이 적용되었다

→ 사실상 glibc 2.26 까지만 tcache dup가 가능하다

 

2. 예제 코드

// Name: df2.c
// Compile: gcc df2.c -o df2

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

int main() {
  char *chunk;
  chunk = malloc(0x50);
  
  printf("Address of chunk: %p\n", chunk);
  
  free(chunk);
  free(chunk); // Free again
}

 

같은 청크를 연속으로 두 번 해제하는 코드이다

 

aborted (core dumped)

코드를 컴파일 하고 실행하면 double free가 감지되어 프로그램이 종료되는 것을 볼 수 있다

 

 

2. tcache 코드 분석 (glibc 2.27)

1. tcache_entry

typedef struct tcache_entry {
  struct tcache_entry *next;
+ /* This field exists to detect double frees.  */
+ struct tcache_perthread_struct *key;
} tcache_entry;

tcache_entry는 해제된 tcache 청크들이 갖는 구조이다

 

double free를 탐지하기 위해 key 포인터가 추가된 것을 확인할 수 잇다

 

2. tcache_put

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]);
}

tcache_put()은 해제한 청크를 tcache에 추가하는 함수이다

 

tcache_put 함수는 해제되는 청크의 key에 tcache라는 값을 대입하도록 변경됐다

→ tcache는 tcache_perthread라는 구조체 변수를 가리킨다

 

3. tcache_get

tcache_get (size_t tc_idx)
   assert (tcache->entries[tc_idx] > 0);
   tcache->entries[tc_idx] = e->next;
   --(tcache->counts[tc_idx]);
+  e->key = NULL;
   return (void *) e;
 }

tcache_get은 tcache에 연결된 청크를 재사용할 때 사용하는 함수이다

 

tcache_get함수는 재사용하는 청크의 key값에 NULL을 대입하도록 변경됐다

 

4._init_free

_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

_int_free은 청크를 해제할 때 호출되는 함수이다

 

재할당하려는 청크의 key값이 tcache이면 Double Free가 발생했다고 보고 프로그램을 abort시킨다

 

그 외의 보호 기법은 없다

→ 조건문만 통과하면 Double Free를 일으킬 수 있다

 

 

3. 동적 분석

1. key 확인

malloc 으로 heap을 할당한 뒤 break 걸고 확인해보자

heap

0x56013b208250에 청크가 할당되었다

 

data 확인

0x61 의 size 로 data 에 아무것도 들어가있지 않은 것을 볼 수 있다

 

tcache_entry

gdb에서 변수를 설정한다

 

청크를 free 한 뒤 확인해보면

key

key 값이 0x556f495d1010 인 것을 알 수 있다

 

chunk

chunk에서 fd 다음 필드가 key인 것을 알 수 있다

 

key 값을 확인해보자

entries

해제한 청크의 주소 0x556f495d1250이 entry에 저장되어 있는 것을 확인할 수 있다
→ 이는 해제된 청크들이 tcache_perthread_struct 내부의 bin entry에 연결되기 때문이다

 

2. 우회 방법

+       /* 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)) // Bypass it!
+         {
+           ...
+             if (tmp == e)
+               malloc_printerr ("free(): double free detected in tcache 2");
+         }
+           ...
+       if (tcache->counts[tc_idx] < mp_.tcache_count)
+         {
+           tcache_put (p, tc_idx);
+           return;
+         }
       }

 

_init_free 함수에서 if (__glibc_unlikely (e->key == tcache)) 만 우회하면 tcache 청크를 double free 시킬 수 있다

→ 해제된 chunk의 key 를 1bit라도 바꿀 수 있다면 우회할 수 있다

 

 

4. tcache dup

1. 우회 코드

// Name: tcache_dup.c
// Compile: gcc -o tcache_dup tcache_dup.c

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

int main() {
  void *chunk = malloc(0x20);
  printf("Chunk to be double-freed: %p\n", chunk);
  
  free(chunk);
  
// chunk + 8 = key 필드 위치
  *(char *)(chunk + 8) = 0xff;
  free(chunk);
  
  printf("First allocation: %p\n", malloc(0x20));
  printf("Second allocation: %p\n", malloc(0x20));
  
  return 0;
}

해제된 청크의 key 값을 1byte 바꾸는 코드이다

 

2. 동적 분석

malloc으로 heap을 할당한 뒤 break를 걸고 확인해보자

heap
chunk

size는 0x31 로 data 가 비어있는 것을 볼 수 있다

 

첫 번째 free를 한 뒤 chunk를 확인해보자

free chunk

key 는 0x55ee7ef30010인 것을 알 수 있다

 

두 번째 free를 하기 직전 chunk를 확인해보자

변조된 key 값

key 값이 0x55ee7ef300ff 로 변조된 것을 볼 수 있다

 

double free

free를 진행하면 정상적으로 되는 것을 볼 수 있다

해제된 청크의 fd가 해제된 청크를 가리키고 있다

→ 오류 없이 double free가 발생했다

 

double free bug

tcache의 double free 방지 코드를 우회하여 double free가 된 것을 볼 수 있다

저작자표시 비영리 변경금지 (새창열림)

'Hacking > Pwnable' 카테고리의 다른 글

DFB(Double Free Bug)  (0) 2025.05.19
UAF (Use After Free)  (0) 2025.05.07
Heap - ptmalloc2 (glibc)  (0) 2025.04.03
Stack Pivoting  (0) 2025.04.02
SROP (x64)  (0) 2025.03.26
  1. tcache duplicate
  2. 1. tcache?
  3. 2. tcache 코드 분석 (glibc 2.27)
  4. 3. 동적 분석
  5. 4. tcache dup
'Hacking/Pwnable' 카테고리의 다른 글
  • DFB(Double Free Bug)
  • UAF (Use After Free)
  • Heap - ptmalloc2 (glibc)
  • Stack Pivoting
GunP4ng
GunP4ng
GunP4ngGunP4ng 님의 블로그입니다.
GunP4ng
GunP4ng
GunP4ng
전체
오늘
어제
  • 분류 전체보기 (104)
    • Hacking (31)
      • Pwnable (30)
      • Web (0)
      • Reversing (0)
      • Misc (1)
    • War Game (21)
      • Pwnable (21)
      • Web (0)
      • Misc (0)
    • pwnable.kr (0)
    • CTF (0)
    • Coding (48)
      • baekjoon (26)
      • C (22)
    • ETC (4)
      • Certificate (4)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
  • 글쓰기

공지사항

인기 글

태그

  • rtl_chaining
  • C언어
  • 구조체
  • RTL
  • x86
  • PIE
  • Dreamhack
  • 백준
  • 코딩도장
  • 포인터
  • Tcache
  • return_to_library
  • Use-After-Free
  • 스택
  • uaf
  • doublefreebug
  • ROP
  • shellcode
  • BOF
  • srop
  • plt
  • Got
  • x64
  • got_overwrite
  • 함수
  • DFB
  • 메모리
  • 코딩
  • tcache_dup
  • WarGame

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.3.0
GunP4ng
Tcache dup
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.