[PWN] Tcache Poisoning

Magnolia·2026년 3월 26일

Tcache Poisoning

Tcache Poisoningtcache freelist에 들어 있는 다음 chunk 주소 (fd)를 조작해서 이후 malloc()이 공격자가 원하는 주소를 반환하도록 만드는 기법이다.

malloc()은 힙에서 정상적인 chunk를 반환해야 하지만 tcache poisoning을 하게되면 malloc()은 힙 chunk가 아니라 임의 주소나 GOT, hook 등을 반환하게 할 수 있다.


Tcache

프로그램은 동적 메모리를 쓸 때 malloc()으로 할당하고 free()로 해제한다.

그런데 매번 메모리를 새로 만들고 완전히 정리하는 방식은 비효율적이기 때문에 glibc는 한 번 사용했던 chunk를 버리지 않고 재사용한다.

이 재사용을 위한 관리 구조가 여러 개 있는데 그 중 성능 향상을 위해 추가된 것이 tcache(Thread Cache)이다.

Tcache는 glibc 2.26부터 도입된 구조로 작은 크기의 free된 chunk를 thread별로 따로 저장해 두는 캐시이다.

특징은 다음과 같다.

  • thread마다 따로 존재
  • size class별로 관리
  • singly linked list 사용
  • malloc / free 속도 향상

어떤 크기의 chunk를 free하면 bin으로 가지 않고 먼저 tcache에 들어간 후, 같은 크기의 malloc이 오면 바로 꺼내준다.

tcache는 size별로 freelist를 가지며, 내부적으로 다음과 같은 구조를 가진다.

  • entries[]는 각 size class의 시작 주소를 저장한다.
  • 각 chunk는 fd를 통해 다음 chunk를 가리킨다.
  • 단일 연결 리스트 구조이다.

그래서 LIFO 구조로 동작한다.


chunk

glibc에서 chunk는 다음과 같이 생겼다.

여기서 prev_sizesize는 metadata로 분류되고, data는 사용자가 실제로 사용하는 user 영역으로 분류된다.

allocated 상태에선 user 영역을 사용자가 자유롭게 사용한다.

chunk가 free되면 glibc는 그 user 영역을 더 이상 사용자 데이터로 보지 않고 freelist 연결 정보 저장용으로 사용한다.

tcache에서는 이 user 영역의 맨 앞부분에 다음 free chunk를 가리키는 포인터인 fd를 저장한다.

"free된 뒤에는 원래 사용자 데이터 공간이 freelist 포인터 저장 공간으로 바뀐다"가 핵심이다.


기본 원리

다시 본론으로 돌아오면, tcache는 free된 chunk의 user 영역에 fd를 저장한다.

그런데 만약 공격자가 취약점을 이용하여 free된 chunk의 user 영역을 다시 쓸 수 있다면 fd도 덮을 수 있다.

즉, free된 chunk에 접근이 가능하면 그 안에 저장된 next pointer를 조작할 수 있고 freelist 연결 구조가 바뀐다.

하지만 최신 glibc에는 보호 기법이 들어가서 익스플로잇이 쉽지 않아졌다.

safe linking

최신 glibc에서는 fd를 평문 주소로 저장하지 않고 xor해서 저장한다.

즉, 메모리에 보이는 값이 진짜 next pointer가 아니기 때문에 공격자가 fd를 조작하려면 단순히 타겟을 쓰는 것이 아니라 safe linking 규칙에 맞는 인코딩 값을 써야한다.



여기에서 tcache에 들어간 chunk의 fd를 보면 정상적인 주소가 아니다. 이것이 safe linking이 적용된 주소이다.

0개의 댓글