본문 바로가기

System Security/Technology

Use after free

Use after free 란?

Use after free 는 동적 할당(Dynamic allocation)을 통해 Heap에 메모리를 할당하여 사용하고 Free를 통해 메모리 해제를 한 후, 다시 같은 크기의 메모리 크기만큼 동적 할당(Dynamic allocation) 할 경우 이전에 사용했던 메모리를 할당해주게 되어 발생하는 취약점입니다.

 

아래 예제를 통해 알아보도록 합시다. 

UAF (Use After Free) 예제

위 예제를 보면, buf 에 128 bytes 만큼의 공간을 힙에 할당해주고 난 후, "A" 10개를 String Copy 하고 Free 해주었습니다. 이후, buf2가 다시 같은 크기 128 bytes 만큼의 공간을 할당한 후, 바로 buf2를 출력합니다. 

 

그런데, 아무것도 입력해주지 않은 buf2가 AAAAAAAAAA를 출력하는 것을 확인할 수 있습니다. 

 

왜 이런 일이 발생하는 것 일까요?

그 이유는 Coalescing policy에 Deferred coalescing (병합 지연) 때문입니다. Coalescing policy에는 Immediate coalescing 과 Deferred coalescing이 존재하는데, 여기서 병합 지연은 만일 특정 크기의 메모리가 힙에 할당된 후 해체되었을 때, 해당 공간을 Free list에 저장하게 되는데 같은 크기 만큼의 메모리를 할당이 요구 되었을 때, 메모리를 효율적으로 사용하기 위해 Free list에서 새로운 공간을 할당해주는 것이 아니라 Free list에서 이전에 사용했던 영역 (Free 되었던 영역)을 할당해주게 됩니다. 

 

이로 인해, Use After Free 취약점이 발생하게 되는 것입니다. 다음 예제를 통해 이 취약점이 어떻게 악용되는지 알아봅시다. 

Code 

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

typedef struct { 
	char name[12] ; 
	void (*print) (void*) ; 
} test; 

void printName(test * t) 
{ 
	printf("printName: %s\n", t->name) ; 
}

void uaf() { 
	printf("Use After Free");  
}

int main(void) { 
	test * t ;
	test * t2 ; 
	
	t = (test *)malloc(sizeof(test)); 
	
	strcpy(t->name, "John"); 
	t->print = (void *)printName; 
	
	t->print(t); 
	free(t) ; 
	
	t2 = (test *)malloc(sizeof(test)) ; 
	scanf("%s", t2->name) ; 
	
	t2->print(t2);  
	
	free(t2); 
	return 0; 
}

위 프로그램은 Structure에 이름을 쓴 후, printName 함수를 test structure에 print 라는 이름의 function pointer에 꼽아주어 이름을 출력하는 프로그램입니다.

 

위 코드에서 문제가 되는 부분은 malloc 이후 free를 해준 후, 다시 같은 크기 만큼 malloc을 진행하게 되어 t = (test *)malloc(sizeof(test)) ; 코드에서 할당 해주었던 메모리 주소를 t2가 사용하게 된다는 것입니다. 

Analysis

위와 같이 컴파일 과정을 거쳐 gdb를 실행시켜줍니다. 

main을 보면 scanf 부분이 있는데, scanf 이후에 브레이크를 걸어보도록 하겠습니다. 

*main+120에 브레이크를 걸어주었는데, 이 부분을 브레이크 포인트로 잡은 이유는 test *t 가 동적 할당이 진행된 후, free(t); 를 통해 메모리 해제가 된 후, test *t2가 동적 할당이 완료된 상태이기 때문에 test *t 에서 사용했던 부분을 test *t2가 다시 사용하는지 확인하기에 적절하기 때문입니다. 

 

그럼, info proc mappings 명령을 통해 heap 메모리에 어떤 부분을 사용하고 있는지 확인해봅시다. 

0x804b000(heap의 시작 주소)을 출력해보면, 0x41414141 이라고 적힌 부분을 볼 수 있는데 이는 아까 test *t2 에서 name에 AAAA를 입력해준 것임을 확인할 수 있습니다. 그런데, 2번째 줄을 보시면 0x80484cb 라고 적힌 것을 볼 수 있는데 이것이 바로 use after free 취약점을 보여주는 것임을 알 수 있습니다. 

 

코드에 test * t 가 t->print = (void *)printName; 를 진행하면서 test * t 의 print (function pointer)가 printName 함수를 가리키는 과정이 있었는데, t2는 이러한 과정이 없었는데 t2의 print (function pointer)가 0x80484cb (printName 함수 주소)를 가리키고 있다는 것을 확인할 수 있습니다. 

 

즉, 앞에서 할당되어 Heap에 쓰였던 데이터가 그대로 남아 있게 된다는 것이죠. 

 

메모리가 이와 같이 재사용 되었을 때 발생하는 취약점을 이용하여 t2의 print (function pointer)가 가리키고 있는 0x80484cb (printName 함수 주소) 부분을 0x80484e8(uaf 함수 주소)로 덮어써봅시다. 

 

Payload 

Dummy(12 bytes) + uaf functon address (4 bytes) 

이와 같이, 해당 부분을 0x80484e8(uaf 함수 주소)로 덮어쓰니 t2->print(t2); 코드 부분에서 uaf 함수가 실행되는 것을 확인할 수 있습니다.

 

간단한 예제를 통해 UAF 발생 시 일어날 수 있는 공격에 대해 알아보았습니다. 

'System Security > Technology' 카테고리의 다른 글

메모리 보호 기법  (0) 2022.02.13
CTF Tools  (0) 2022.02.13
GOT Overwrite  (0) 2022.01.22
NOP Sled  (0) 2022.01.19
스택 프레임(Stack Frame)이란?  (0) 2022.01.18