취약점분석/Pwnable

[Pwnable] Tcache Poisoning

poiri3r 2025. 11. 5. 21:23

오늘은 Tcache Poisoning공격에 대해 공부해보겠습니다.

Tcache Poisoning은 말 그대로 tcache를 조작하여 임의 주소에 청크를 할당시키는 공격 기법입니다.

 

Poisoning은 free()된 청크의 fd(forward pointer)를 덮어써서, malloc()이 힙이 아닌 스택 주소를 반환하도록 조작합니다.

해당 취약점은 UAF(Use-After-Free)에서 기인합니다.

 

how2heap에서 제공하는 코드를 먼저 살펴보겠습니다.

https://github.com/shellphish/how2heap/blob/master/glibc_2.41/tcache_poisoning.c

 

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

int main()
{
	// disable buffering
	setbuf(stdin, NULL);
	setbuf(stdout, NULL);


	size_t stack_var[0x10];
	size_t *target = NULL;

	for(int i=0; i<0x10; i++) {
		if(((long)&stack_var[i] & 0xf) == 0) {
			target = &stack_var[i];
			break;
		}
	}
	assert(target != NULL);

	//청크 2개 할당
	intptr_t *a = malloc(128);
	intptr_t *b = malloc(128);

	free(a);
	free(b);

	b[0] = (intptr_t)((long)target ^ (long)b >> 12);
	
    intptr_t *c = malloc(128);

	printf("We got the control\n");

	assert((long)target == (long)c);
	return 0;
}

 

코드가 좀 길어서 printf문은 제거했습니다.

준비 단계에서는 0x80의 크기의 a와 b를 할당한 뒤 free(a)와 free(b)를 통해 tcache에 넣습니다

(glibc는 헤더길이 포함 0x90 크기 bin서 관리)

 

Tcache는 LIFO(후입선출)이므로 b가 리스트의 head가 됩니다.

즉 tcache list[0x90]에서는 [ b -> a ] 상태로 b의 next 포인터가 a를 가르키게 됩니다.

 

b[0] = (intptr_t)((long)target ^ (long)b >> 12);

 

UAF취약점을 이용한 공격 포인트입니다.

b는 이미 해제되었지만, b[0] (b 청크의 데이터 영역 첫 8바이트)에 접근합니다.

해제된 tcache 청크의 첫 8바이트는 fd 포인터이므로 , 공격자는 b의 fd 포인터(a를 가르킴)에 원하는 임의의 주소를 덮어 쓸 수 있습니다.

Tcache List에서 [ b -> Target Address ] 의 형태로 원하는 주소를 가르키게 할 수 있습니다

printf("1st malloc(128): %p\n", malloc(128));

 

malloc은 tcache 0x90 bin의 맨 앞에 있는 청크 b를 반환합니다

또한 malloc은 tcache list의 head를 b에서 b의 fd포인터인 Target Address로 업데이트 합니다

intptr_t *c = malloc(128);

 

다시 malloc(128)을 통해 동적 할당 진행시 tcache는 해당 주소를 반환하고 c 포인터는 스택 주소를 가지게 됩니다.

 

assert((long)target == (long)c);

 

assert는 논리적인 함정으로 target과 c가 같은 값을 가지지 않으면 assertion failed와 함께 프로그램을 강제종료합니다.

하지만 해당 c코드를 실행시켜보면 정상적으로 실행됨을 확인할 수 있습니다.

사진을 보시면 2th malloc에 힙 주소인 0x56 ... 이 아니라 스택주소 0x7ff 의 주소가 적혀있음을 확인할 수 있습니다.

(해당 코드는 최신 glibc에선느 Safe-Linking에 의해 실행이 막힙니다)

 

이상으로 Tcache Poisioning에 대해 알아봤습니다.

다음 포스팅에서는 드림핵 3단계 Tcache Poisioning문제에 대한 write up을 작성해보겠습니다.

읽어주셔서 감사합니다!