/ POSTS

linux_0x07 UAF(Use After Free) - 1부

UAF(Use After Free) - 1부

안녕하심꽈 Sulla임돠.

오늘은 UAF(Use After Free)라는 Heap영역에서 발생하는 버그를 이용한 공격 기법을 알아 보겠습니다.

앞 선 포스트에서 Heap 구조를 설명 드릴 때 malloc() 함수 등으로 Heap 영역을 할당 받은 후에 반드시 해야하는 행위가 있다 했습니다. 바로 Free()라고 하는 함수를 사용해서 할당 받은 Heap영역을 해제 해야 한다고 알려 드렸습니다. 할당 받고 다 쓴 영역을 다시 반납하는 과정이지요. (도서관에서 책을 빌려보고 다 봤으니 반납하는 책 대여 시스템을 생각하시면 됩니다.) 그런데 왜 이 짓거리에서 버그가 발생하고 이를 이용해 취약점으로 사용할까요?

언제나 그랬듯 이해를 위한 간단한 그림으로 알아보도록 하겠습니다.

[그림 7-1]

위 그림에서 보이듯 A 이름으로 malloc()으로 8byte 할당 받고 “AAAA”를 저장해 두고 A를 출력 하면 A에 저장해 두었던 “AAAA” 가 출력 될것입니다.

그 다음 할당 받은 A를 free()로 해제 시켜준 후 같은 8byte로 B이름으로 할당 받았습니다. 이 때 A에서와는 다르게 문자를 저장 하지 않았습니다.

이 상태에서 B를 출력 하면 어떤 값이 나올까요????

[그림 7-2]

AAAA가 됩니다. 앞에서 저장 했던 chunk A에 저장 되있던 AAAA가 free를 한 후에도 사라지지 않고 그대로 남아있다가 같은 크기로 새롭게 할당받은 영역 B에서까지 그대로 남아있는 상태며 출력까지도 되는 모습입니다.

정리하자면, A를 malloc()으로 할당 하여 chunk를 생성하고 A에 12345를 저장 후 free()로 A를 해제하고 B를 A와 같은 크기로 malloc()을 이용해 chunk를 생성한다. 이 때 12345는 저장 하지 않은 상태로 B에 저장된 값을 호출하면 A에 저장되어있던 12345가 출력됩니다. free()로 해제한 상태인데도 기존에 저장되어있던 값이 그대로 호출 됩니다. 주소값 또한 B의 chunk 주소는 이미 free()로 해제한 A의 chunk주소와 동일한 상태입죠.

두 가지의 이유로 저렇게 같은 공간으로 재할당 받게 됩니다.

  1. 효율 적인 공간 관리를 위하여 병합 지연(Deferred Coalescing)이라고 하며 해제된후 다시 할당 받을 때 Heap 영역을 병합, 분할하는 시간을 줄이기 위해서 해제된 영역을 그대로 보관 합니다.
  2. 메모리 할당 알고리즘 최소 적합(first fit)이라고 하며 Heap은 해제를 효율적으로 운영하기 위해 Bin이라는 구조를 사용 합니다. 즉 할당 받은 영역을 free하게 되면 Bin에 저장된다 생각하시면 됩니다. 이 Bin에서 사용되는 알고리즘 중 하나가 최소 적합 알고리즘이며(first fit 외에 다른 알고리즘도 존재 합니다.) 해당 알고리즘의 특징은 여러가지가 있지만 가장 큰 특징은 대부분의 상황에 적합/최적 알고리즘이란 것이죠. 아래는 몇 가지 특징입니다만
    • 사용 가능한 공간중 첫 번째 공간을 사용
    • 가장 최근 검색이 끝난 위치에서 시작
    • Unsorted Chunk List 형태로 cache 처럼 동작
    • ……
    • Anytime, Anywhere, Not Bad, So Good

    최소 적합 알고리즘은 해제된 Chunk list를 Cache형태로 관리 하며 동일 크기 또는 비슷한 크기의 할당 요청(malloc())하면 bin에 저장된 해제된 Chunk list를 검색하여 사용 가능한 첫 번째 공간을 바로 할당 합니다.

아래 예제 코드로 테스트 해보겠슴돠!!

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

int main(void)
{
    int* A;
    int* B;
    int* C;
    int* D;

    A = (int*)malloc(256);
    B = (int*)malloc(256);

    printf("1. malloc A addr & data : %p, %#x\n", A, *A);

    *B = 0x1234abcd;
    printf("2. malloc B addr & data : %p, %#x\n", B, *B);

    free(B);
    printf("[free malloc B]\n");

    C = (int*)malloc(256);
    printf("3. malloc C addr & data : %p, %#x\n", C, *C);

    D = (int*)malloc(1024);
    printf("4. malloc D addr & data : %p, %#x\n", D, *D);

    return 0;
}

A, B, C, D 포인터 생성하고 A와 B에 malloc(256)을 할당해주고 A의 주소와 데이터를 출력 합니다. 다음으로 B에 값을 입력해주고 마찬가지로 주소와 데이터를 출력해줍니다. 이번에는 B를 해제해주고 C를malloc(256) 항당하고 주소와 데이터를 출력합니다. 다음은 D의 malloc 을 할당 해주지만 용량을 1024로 증가시켜주고 주소와 데이터를 출력해주는 코드입니다.

위 코드를 실행할 경우 어떤 결과가 나올지 상상해보며 낭낭하게 직접 실행해 보겠습니다.

[그림 7-3]

1번의 malloc A의 주소와 데이터와 2번의 malloc B의 주소와 데이터를 보시면 다르다는걸 확인할 수 있습니다. 그다음 B를 해제해주고 같은 용량의 malloc C를 할당했을 때의 주소와 데이터를 확인하시면 B와 같음을 확인할 수 있으며 malloc D의 경우 용량이 달라지니깐 주소값도 데이터도 달라짐을 확인 가능합니다.

중요한건 B의 free 후에 생성된 동일 용량의 C를 확인 했을때 입니다. C를 같은 용량으로 할당만 했을뿐 데이터의 입력은 없었는데 이미 해제한 B의 데이터가 여전히 출력 됩니다.

위 예제로 포인터가 이미 해제된 영역을 여전히 가리키는 현상을 확인할 수 있습니다. 이러한 현상을 Dangling Pointer라고 부릅니다. (현실에는 없는 마음속의 공간이나 존재 하지 않는 상상속의 여친을 생각하세요……뭐요!?) 지금 알아보고 있는 UAF(Use After Free)Dangling Pointer를 재사용 함으로서 생기는 것이죠.

간단하게, 결국 UAF잘못된 포인터 관리로 생기는 취약점입니다.

말이 길어졌습니다만 어쨌든 Heap 영역에서 할당 받은 후 해제 시 해당 영역을 초기화 하지 않을 경우 데이터가 의도치 않게 계속 남아 있는 상태가 됩니다. 이 점과 지금껏 해왔던 것을 잘 짜집기 하면 원하는 방향으로 공격이 가능하지 않을까 싶습니다.

지금까지 장황하게 UAF에 대한 내용을 다뤄봤습니다. 다음 포스팅에서 이어서 실제 shell을 따도록 하겠습니다. 사실 이번 포스팅 작성 하다가 한 번 날려 먹어서 멘탈도 같이 날려 먹었습니다….. 그래서 두 편을 나누는건 아닙니다….급하게 마무리 하는듯 하지만 아닙니다…….뭐요?

빠른 시일내에 다음 포스팅 들고 뵙겠습니다.

수고하셨습니다.