ENFJ_Blog
Use-After-Free(UAF) 본문
https://learn.dreamhack.io/106
로그인 | Dreamhack
dreamhack.io
Use-After-Free (UAF)
해제된 메모리에 접근할 수 있을 때 발생하는 취약점을 말한다
원인은 메모리 참조에 사용한 포인터를 메모리 해제 후에 적절히 초기화하지 않아서, 또는 해제한 메모리를 초기화하지 않고 다음 청크에 재할당해주면서 발생하는 취약점이다.
Dangling Pointer
Dangling Pointer는 유효하지 않은 메모리 영역을 가르키는 포인터를 말한다.
예시) 동적 할당에 사용하는 malloc 함수는 할당한 메모리 주소를 반환한다. 그래서 일반적으론 메모리를 동적 할당 할 때 포인터를 선언 후 그 포인터에 malloc 함수가 할당한 메모리 주소를 저정한다. 그리고 그 포인터를 참조하여 메모리에 접근한다. 메모리를 해제할 때는 free 함수를 사용하게 되는데 free 함수는 ptmalloc에 청크를 반환하기만 할 뿐, 청크를 담고 있는 포인터를 초기화 하지 않는다. 그로인해 free 함수 이후에 프로그래머가 포인터를 초기화 하지 않으면, 포인터는 해제된 청크를 가르키는 Dangling Pointer가 된다.
Dangling Pointer가 생긴다고 해서 보안이 취약한 것은 아니나, 프로그램이 예상치 못한 방향으로 동작할 수 있고, 경우에 따라 공격 수단으로 활용될 수 있다.
UAF
malloc과 free 함수는 할당 또는 해제할 메모리에 데이터를 초기화 하지 않는다. 그래서 새롭게 할당한 청크를 프로그래머가 초기화 하지 않으면, 메모리에 남아있던 데이터가 유출되거나 사용될 수 있다.
예시 코드
// 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();
}
}
코드 분석
실행 흐름
구조체 Secret 와 구조체 NameTag 선언 secret을 할당해준 뒤 secret에 malloc으로 할당해준 뒤 secret_name에 "ADMIN PASSWORD" 복사를 해준다 그 뒤 secret_info 에 "P@ssw0rd!@#" 이라는 비밀 문자를 입력하고 secret 안에 있는 code에 0x1337이라는 16진수를 안에 넣어준다.
free로 해제를 해주고 secret을 초기화 해준다.
nametag로 NameTag 구조체를 할당해주고, team_name에 "security team"이라는 문자를 저장하고, name에는 "S"를 넣어준다. 그 뒤 team name과 name을 출력해준다. 마지막으로 func를 출력해준다.
실행결과
분석
ptmalloc2 는 새로운 할당 요청이 들어왔을 때, 요청된 크기와 비슷한 청크가 bin이나 tcache에 있는지 확인하고, 만약 있다면, 해당 청크를 꺼내 사용한다. Nametag와 Secret은 같은 크기의 구조체이기 때문에 앞서 할당한 secret을 해제하고 nametag를 할당하면, nametag는 secret과 같은 메모리 영역을 사용하게 된다. 이때 free는 청크를 반환하기만 할 뿐이지 초기화는 하지 않기 때문에 같은 영역을 사용하게 되는 nametag에는 secret의 값에 일부가 남아있게 된다. 실행결과 처럼 "P@ssw0rd!@#"에서 입력한 "S"만 바뀌는 "S@ssw0rd!@#"가 나오게 되고 함수 포인터의 0x1337이라는 값이 나오게 된다.
gdb 분석
먼저 우리는 free에 값이 남아있는 지, 그것을 다시 사용하게 되는지 확인하기 위해서 이니 free 후 b *main+112로 브레이크 포인트를 건다.
실행한 뒤 heap 명령어(pwndbg만 됨 peda는 다른 명령어일걸)를 통해 free 된 청크를 확인한다.
Free 주소에 있는 해제된 secret의 영역 확인 그리고 그 주소에 있는 값을 확인한다.
secret_name에 해당하는 부분은 적절한 fd와 bk값으로 초기화됐지만, secret_info에 해당하는 부분은 값이 그대로 남아있는 모습을 확인할 수 있다.
그 뒤 nametag를 할당한 뒤 printf를 호출하는 시점의 변수들의 값을 확인한다.
$ b *main+222
$ c
뒤
위와 같이 같은 주소에 securty team이라는 글자가 들어간 걸 확인할 수 있다.
하지만 x/s를 보면 " S@ssw0rd!@#"이라는 문자가 나오고 그 밑에 있는 주소 0x4052d0에는 위와 같은 0x1337이 들어있는 걸 확인 할 수있다.
그리고 nametag->func가 호출되서 Sagmentation Fault가 발생했다.
위와 같이 동적 할당 후 메모리 초기화를 해주지 않으면 해제를 해도 객체의 데이터가 남는다. 이러한 특징을 공격자가 이용한다면 초기화되지 않은 메모리의 값을 읽어내거나, 새로운 객체가 악의적인 값을 사용하도록 유도하여 프로그램의 정상적인 실행을 방해할 수 있습니다.
'Pwn' 카테고리의 다른 글
[White Hat School] a_piece_of_pie (0) | 2024.03.28 |
---|---|
[dreamhack wargame] Gaia문제 (0) | 2024.01.25 |
ptmalloc - linux 메모리 할당 ( Memory Allocator ) (0) | 2023.10.19 |
Command Injection (2) | 2023.10.11 |
개발자의 실수 - Type Error (0) | 2023.09.29 |