tcache_dup

A double free puts the same chunk in the tcache list twice, so a later malloc hands back a pointer we control.

2026.07.17 Pwn original post

드림핵에서 미해결 문제를 보는데 tcache 라는 키워드가 들어간 문제가 쭈르륵 있어서 셋다 풀어보기로 함.

Figure 1

tcache가 들어간 워게임 문제는 힙(Heap) 익스플로잇 문제의 대표적인 유형이에요. 시스템 해킹 배우실 때 결국 마주치 게 되는 핵심 개념 중 하나예요. tcache가 뭔지 먼저 간단히 **tcache(Thread Local Cache)**는 glibc(리눅스 표준 C 라이브러리)가 malloc/free를 더 빠르게 처리하려고 만든 캐시 시스템이에요 free()로 메모리를 해제하면, 그 메모리 조각 이 바로 OS로 반환되는 게 아니라 **tcache라는 “재활용 대기열”**에 잠깐 보관돼요 다음에 비슷한 크기로 malloc()을 요청하면, 이 대기열에서 빠르게 꺼내줘서 속도가 빨라져요 왜 tcache가 해킹의 표적이 될까? 이 “재활용 대기열” 구조 자 체에 검증이 허술한 부분이 있어서, 공격자가 여기를 조작하면 원하는 메모리 주소에 원하는 값을 쓸 수 있게 돼요.

즉, 메 모리를 마음대로 조작할 수 있는 강력한 무기가 되는 거죠. 워게임에서 자주 나오는 tcache 문제 유형

  1. Double Free 같 은 메모리를 free()로 두 번 해제하는 취약점 tcache 대기열에 같은 조각이 중복으로 들어가게 만들어서, 이후 이를 이용 해 메모리 조작
  2. Tcache Poisoning free()된 메모리 조각의 내부 데이터(다음 조각을 가리키는 포인터)를 조작 그러면 다음 malloc() 요청 시, 공격자가 원하는 임의의 주소를 메모리로 받아올 수 있음 이게 되면 got 테이블 변조, 스택 조작 등으로 이어져서 결국 셸 획득까지 가능
  3. Use-After-Free (UAF) + tcache free()한 메모리를 실수로 계속 참조하는 취 약점을 tcache 구조와 결합해서 공격 실제 문제에서 보통 이런 흐름이에요 1.

프로그램이 malloc/free를 자유롭게 호출 할 수 있는 CLI 메뉴 제공 (추가/삭제/수정 등) 2. 취약점(double free 등)을 이용해 tcache 대기열을 조작 3. 원하는 주소 (예: got 테이블, 특정 함수 포인터)에 임의 값을 쓰기 4. 프로그램 흐름을 조작해서 셸(system(“/bin/sh”) 등) 실행 음 읽어봤는데도 아직도 잘이해를 못함… 뭐 3개나 풀다보면 이해가 되지 않을까? 먼저 젤 낮은 레베루 부터

Figure 2

// gcc -o tcache_dup tcache_dup.c -no-pie
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

char *ptr[10];

void alarm_handler() {
    exit(-1);
}

void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGALRM, alarm_handler);
    alarm(60);
}

int create(int cnt) {
    int size;
    if (cnt > 10) { return -1; }
    printf("Size: ");
    scanf("%d", &size);
    ptr[cnt] = malloc(size);
    if (!ptr[cnt]) { return -1; }
    printf("Data: ");
    read(0, ptr[cnt], size);
}

int delete() {
    int idx;
    printf("idx: ");
    scanf("%d", &idx);
    if (idx > 10) { return -1; }
    free(ptr[idx]);
}

void get_shell() {
    system("/bin/sh");
}

int main() {
    int idx;
    int cnt = 0;
    initialize();
    while (1) {
        printf("1. Create\n");
        printf("2. Delete\n");
        printf("> ");
        scanf("%d", &idx);
        switch (idx) {
            case 1: create(cnt); cnt++; break;
            case 2: delete(); break;
            default: break;
        }
    }
    return 0;
}

Figure 3

double free 취약점이 있다. delete()를 보면, free(ptr[idx]); 후에 ptr[idx] = NULL; 를 하지 않는다.

따라서

Create   -> ptr[0] = malloc(...)
Delete 0 -> free(ptr[0])
Delete 0 -> free(ptr[0])   // 같은 포인터를 또 free

가 가능하다. 아직 문제에 대해 너무 헷갈려 찾아보니까, 뜻을 이렇게 정리할수있었다.

크기 0x20짜리 청크를 하나 만들었다고 가정.

A = malloc(0x20);

힙: A 이제 free(A); 를 호출하면 메모리가 사라지는 게 아니다. 실제로는: free(A) A가 tcache에 들어감. 그리고 Tcache 는 그 냥 스택처럼 활용된다. 예시로는 free(A); free(B); free(C); 를하면

Tcache 내부는 이렇게 된다:

Top C B A Bottom

그 다음 free 한 후 x = malloc(0x20); 을 하면, malloc은 tcache 맨 위의 청크를 꺼내줘서 x == c 가 되는것이다. 또한 get_shell() 함수가 있다. 보통에 스택 익스플로잇 문제에서 이런 의미는, get_shell() 함수의 조수를 eip 에 넣어 프 로그램의 플로우를 컨트롤 하라는 뜻이다. 하지만 힙 익스플로잇 에서는 eip 를 건드리는게 아니라, eip 가 나중에 참조할 포인터를 조작한다는것이다. 그러니까

원래는 fp -> puts 인데, 힙 취약점으로 fp -> get_shell 로 바꾸면. 나중에 fp(); 가 실행될 때 쉘이 뜨는것이다. 이 문제는, gcc -no-pie 로 컴파일 됬다. PIE가 꺼져 있으면, get_shell 주소 GOT 주소 BSS 주소 같은 주소들이 고정 된다.

이 문제는 아직 풀지 못했다. tcache 구조를 더 공부한 뒤 다시 시도할 예정이다.

#heap#tcache