[Dreamhack] Use After Free: 2 - Use After Free

securitykss·2023년 1월 24일
0

Pwnable 강의(dreamhack)

목록 보기
31/58

https://dreamhack.io/lecture/courses/106 을 토대로 작성한 글입니다.

1. Introduction

ptmalloc2를 이용해 메모리를 할당 혹은 해제를 한다.

하지만, 메모리에는 할당 또는 해제의 흔적들이 남아 있을 수 있다.

Use-After-Free(UAF)는

메모리 참조에 사용한 포인터를 메모리 해제 후에 적절히 초기화 하지않아서, 혹은 해제한 메모리를 초기화하지 않고 다음 청크에 재할당해주면서 발생하는 취약점이다.

2. Use After Free

2.1 Dangling Pointer

컴퓨터 과학에서, Dangling Pointer는 유효하지 않은 메모리 영역을 가리키는 포인터를 말한다.

메모리의 동적 할당에 사용되는 malloc 함수는 할당한 메모리의 주소를 반환한다.

일반적으로, 메모리를 동적 할당할 때는 포인터를 선언하고, 그 포인터에 malloc 함수가 할당한 메모리의 주소를 저장한다.

그 후 그 포인터를 참조하여 할당한 메모리에 접근한다.

메모리를 해제할 때는 free 함수를 호출한다.

그런데 free 함수는 청크를 ptmalloc에 반환하기만 하고, 청크의 주소를 담고 있던 포인터는 초기화 하지 않는다.

따라서 free의 호출 이후에 프로그래머가 포인터를 초기화하지 않으면, 포인터는 해제된 청크를 가리키는 Dangling Pointer가 된다.

Dangling Pointer가 생긴다고 해서 보안적으로 취약하진 않다.

하지만, Dangling Pointer는 프로그램이 예상치 못한 이슈를 발생시킬 수 있고, 공격자에게 공격 수단으로 활용될 수 있다.

2.2 Use After Free

UAF는 문자 그대로, 해제된 메모리에 접근할 수 있을 때 발생하는 취약점이다.

malloc과 free 함수는 할당 또는 해제할 메모리의 데이터들을 초기화하지 않는다.

그래서 새롭게 할당한 청크를 프로그래머가 명시적으로 초기화하지 않으면, 메모리에 남아있던 데이터가 유출되거나 사용될 수 있다.

2.2.1 PoC


// 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;
  
  // 1. secret 
  
  // secret 동적할당
  // 그리고 각각 구조체 요소에 값을 대입
  secret = malloc(sizeof(struct Secret));
  strcpy(secret->secret_name, "ADMIN PASSWORD");
  strcpy(secret->secret_info, "P@ssw0rd!@#");
  secret->code = 0x1337;

  // secret 해제 후, NULL 로 초기화
  free(secret);
  secret = NULL;
  
  // 2. nametag
  
  // nametag 동적할당
  // team_name과 name에 각각 값 입력
  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);

  // nametag의 요소 중 하나인 func이 NULL이 아니라면
  // 포인터 출력
  if (nametag->func) {
    printf("Nametag function: %p\n", nametag->func);
    nametag->func();
  }
}

2.2.2 실행

nametag의 func에 값을 할당한 적이 없지만,

0x1337이 출력(secret에서 입력한 값)

2.2.3 설명

ptmalloc2는 새로운 할당 요청이 들어왔을 때,(nametag 할당 요청)

요청된 크기와 비슷한 청크가 bin이나 tcache에 있는지 확인한다.(Nametag와 비슷한 크기의 Secret 구조체)

예제 코드에서 Nametag와 Secret는 같은 크기의 구조체 이므로,

앞서 할당한 secret을 해제하고, nametag를 할당하면,

nametag는 secret과 같은 메모리 영역을 사용하게 된다.

이 때, free는 해제한 메모리의 데이터를 초기화하지 않으므로, nametag에는 secret의 값이 일부 남아있게 된다.

2.2.4 동적 분석

1. secret 해제

nametag가 해제 된 후에 breakpoint를 걸고 분석해 보자

heap 명령어를 통해, secret 해제된 청크의 부분을 보자

secret 해제 전

secret 해제 후

해제 전, 후를 비교해 보면, fd와 bk의 값들은 (0x4052a0 부분) 값이 변경됨을 알 수 있다.

하지만 secret_info(0x4052b0)의 값은 그대로 남아 있다.

2. nametag 할당

이제 nametag의 값들이 할당되고, 출력되기 이전에 값들을 확인해 보자

printf를 호출하는 부분에 breakpoint를 걸면, 어떤 값들이 할당되었는지 확인이 가능하다.

nametag 값들 할당 후

nametag->team_naem에는 "security team" 입력이 되었고, ("security team")

nametag->name에는 초기화되지 않은 secret_info의 값이 존재하는 것을 확인할 수 있다.("S@ssw0rd!@#")

또한, nametag->func 위치에 secret->code에 대입했던 0x1337 값이 남아 있다.

이 값이 0이 아니므로 예제의 if(nametag->func) 부분에서 Segementation Fault가 발생한다.

이렇게, 동적 할당한 청크를 해제한 뒤에는 해제된 메모리 영역의 이전 객체의 데이터가 남는 것을 알 수 있었다.

이런 특징을 공격자가 이용한다면 초기화되지 않은 메모리의 값을 읽어내거나, 새로운 객체가 악의적인 값을 사용하게 유도하여 프로그램의 정상적인 실행을 방해할 수 있다.

마치며

Dangling Pointer: 해제된 메모리를 가리키고 있는 포인터. UAF가 발생하는 원인이 될 수 있음.

Use-After-Free (UAF): 해제된 메모리에 접근할 수 있을 때 발생하는 취약점

Reference

https://dreamhack.io/lecture/courses/106

profile
보안 공부를 하는 학생입니다.

0개의 댓글