1. Use-After-Free
- 메모리 참조에 사용한 포인터를 메모리 해제 후, 제대로 초기화하지 않아 발생하는 취약점이다.
해제한 메모리를 초기화하지 않고 다음 청크에 재할당해주기 때문이다. - 즉, 해제된 메모리에 접근할 수 있을 때, 발생하는 취약점이다.
Chunk(청크) 란? • malloc에 메모리 할당을 요청하면, 넓은 메모리의 영역을 다양한 크기의 덩어리(chunk)로 나눈다. • chunk는 사용중인 덩어리, 해제된 덩어리, Top 덩어리, Last Remainder 덩어리가 있다. • 위에서 말하고 있는 청크는 "해제된 덩어리"로 응용프로그램에서 시스템에 반환한 덩어리를 의미하는 것이다. |
2. Dangling Pointer
2-1. 정의
- 컴퓨터 과학에서 'Dangling Pointer'는 유효하지 않은 메모리 영역을 가리키는 포인터이다.
- malloc 함수를 통해,메모리를 할당하고, 반환되는 할당된 메모리 주소를 포인터에 저장한다.
이를 통해, 해당 포인터를 참조하여 할당된 메모리에 접근하는 것이다. - free 함수를 통해 할당한 메모리를 해제하는데, free함수는 청크를 ptmalloc에 반환하기만 하고 청크 주소를 담고 있던 포인터를 초기화하지 않는다.
- free호출 이후에 프로그래머가 포인터를 초기화하지 않을 경우, 포인터는 해제된 청크를 가리키는 'Dangling Pointer'가 된다.
2-2. 예제
(1) 코드
// Name: dangling_ptr.c
// Compile: gcc -o dangling_ptr dangling_ptr.c
#include <stdio.h>
#include <stdlib.h>
int main() {
char *ptr = NULL;
int idx;
while (1) {
printf("> ");
scanf("%d", &idx);
switch (idx) {
case 1:
if (ptr) {
printf("Already allocated\n");
break;
}
ptr = malloc(256);
break;
case 2:
if (!ptr) {
printf("Empty\n");
}
free(ptr);
break;
default:
break;
}
}
}
(2) 코드 분석
- ptr이라는 포인터에 256byte만큼 동적할당을 한다.
- free로 청크를 해제한 후, 청크를 가르키던 ptr변수를 초기화하지 않는다.
- ptr을 초기화하지 않음으로써, 해제한 주소를 가르키는 Dangling Pointer가 된다.
- ptr이 해제된 청크 주소를 가르키므로, 이를 다시 해제할 수 있다. 이러한 취약점을 'Double Free Bug'라고 한다.
3. Use After Free 실습
- UAF취약점은 Dangling Pointer로 인해 발생하기도 하지만, 새롭게 할당한 영역을 초기화하지 않고 사용하면서 발생하기도 한다.
- malloc과 free함수는 할당 또는 해제할 메모리의 데이터들을 초기화해주지 않는다.
따라서, 새롭게 할당한 청크를 명시적으로 초기화하지 않으면, 남아있던 데이터가 유출되거나 사용될 수 있다.
3-1. 예제 코드
// Name: uaf.c
// Compile: gcc -o uaf uaf.c -no-pie
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct NameTag {
char team_name[16];
char name[32];
void (*func)();
};
struct Secret {
char secret_name[16];
char secret_info[32];
long code;
};
int main() {
int idx;
struct NameTag *nametag;
struct Secret *secret;
secret = malloc(sizeof(struct Secret));
strcpy(secret->secret_name, "ADMIN PASSWORD");
strcpy(secret->secret_info, "P@ssw0rd!@#");
secret->code = 0x1337;
free(secret);
secret = NULL;
nametag = malloc(sizeof(struct NameTag));
strcpy(nametag->team_name, "security team");
memcpy(nametag->name, "S", 1);
printf("Team Name: %s\n", nametag->team_name);
printf("Name: %s\n", nametag->name);
if (nametag->func) {
printf("Nametag function: %p\n", nametag->func);
nametag->func();
}
}
3-2. 코드 분석
- 구조체로 NameTag, Secret이 정의되어 있다.
- Secret 구조체를 할당한 후, secret_name, secret_info, code에 값을 입력하고 해제한다.
- NameTag 구조체를 할당한 후, team_name, name에 각각 값을 입력하고 해당 데이터를 출력한다.
- 함수 포인터 func가 NULL이 아니면, 포인터가 가리키는 주소를 출력하고, 해당 주소의 함수를 호출한다.
- 실행결과를 살펴보면, Name으로 secret_info의 문자열이 출력되고, Name과 Nametag에는 입력하지 않은 값이 출력되는 것을 확인할 수 있다.
3-3. 실습과정 (uaf 동적 분석)
- ptmalloc2는 새로운 할당 요청이 들어왔을 때, 요청된 크기와 비슷한 청크가 bin이나 tcache에 있는지 확인하고, 있다면 해당 청크를 꺼내 재사용한다.
tcache & bin 이란? • tcache는 해제된 청크를 크기별로 7개씩 저장해둔 공간으로, bin보다 더 빠르게 비슷한 크기의 청크를 찾아 재할당한다. • tcache가 차면 bin이라는 공간에 해제된 청크가 저장되며, 이는 자료구조 형식으로된 알고리즘으로 복잡하게 되어 있어 속도는 느리다. |
(1) 예제 코드에서 NameTag와 Secret은 같은 크기의 구조이다.
- Secret을 해제하고 NameTag를 할당하면, ptmalloc2를 통해 nametag는 secret과 같은 메모리 영역을 사용하게 된다.
- free함수는 해제한 메모리의 데이터는 초기화지 않기 때문에, secret의 값이 일부 남아있기 때문에 문제가 발생한다.
(2) gdb를 통해, nametag에 secret과 같은 메모리 영역이 할당되는 것을 확인할 수 있다.
① free직후에 break point를 설정 후, 실행한다.
- secret 해제 직후, secret이 사용하던 메모리 영역을 출력하면 확인할 수 있다.
② heap명령어를 통해, 해제된 청크들의 정보를 조회한다.
- heap명령어는 pwndbg에서 지원하는 것으로, 할당 및 해제된 청크들의 정보를 조회하는 명령어이다.
③ 해제된 메모리 영역을 출력해보면, fd와 bk값은 초기화 됐지만, secret_info의 값은 그대로 남아있는 것을 볼 수 있다.
※ 참고 • fd (Forward pointer) : 다음 Free Chunk의 포인터를 가진다. • bk (Backward pointer) : 이전 Free Chunk의 포인터를 가진다. • malloc()은 각 chunk를 관리하기 위해, malloc_chunk구조체를 선언한다. • malloc_chunk의 구조체를 보면, size, size, fd, bk ,,,등의 순서대로 작성되어 있으며, fd와 bk는 free된 메모리에만 존재한다. |
④ name tag를 할당하고, printf 함수를 호출하는 부분에 break포인트를 설정하고, 실행한다.
- 'security team'이 0x602260에 저장되어 있는 것을 보아, 해제된 메모리가 재할당된 것을 확인할 수 있다.
⑤ nametag의 메모리 영역을 출력한다.
- nametag -> team_name에는 "security team"이 그대로 입력되었으나, namgetag -> name에는 초기화되지 않은 secret_info의 값이 존재하는 것을 확인할 수 있다.
- nametag->func 위치에도, secret->code에 대입했던 0x1337이 남아있는 것을 확인할 수 있다.
3-4. 결론
- 동적할당한 청크를 해제한 뒤에도 해제된 메모리 영역에 이전 객체의 데이터가 남는다.
- 이러한 문제점을 통해, 초기화되지 않은 메모리의 값을 읽어내거나, 새로운 객체가 악의적인 값을 사용하게 유도하여 프로그램의 정상적인 실행을 발해할 수 있다.
'Dreamhack > Lecture & Practice' 카테고리의 다른 글
[Practice] Use After Free (0) | 2022.09.16 |
---|---|
[Practice] 64bit_FSB (Format String Bug) (0) | 2022.08.20 |
[Lecture] Format String Bug (FSB) (0) | 2022.08.10 |
[Lecture] OOB (Out of bounds) (0) | 2022.08.09 |
[Practice] Hook Overwrite (0) | 2022.07.14 |