Hubeen's Home

Use After Free

오늘 다뤄볼 취약점은 Use After Free입니다.

말 그대로 사용한 후 메모리를 해제했을 때에 취약점을 발견할 수도 없을 수도 있을 수도 있습니다.

이 취약점은 근래에 브라우저 취약점에서 많이 발견되었습니다.


[CVE-2012-4792 IE Use-After-Free Analysis and Exploit]

- http://pgnsc.tistory.com/348


멋쟁이 sweetchip님의 BoB 프로젝트 도중에 그 당시에 문서로 남겨놓으셨습니다!


이러한 UAF 취약점은 스택 영역에서 이루어지는 취약점이 아닌 힙 영역에서 발생하는 취약점입니다.

What is Heap?

네, 그렇다면 heap은 무엇일까요


잘 빠지고 이쁜 엉덩이를 말하는 걸까요?
아쉽게도 이런 이쁜 엉덩이를 원하셨다면, 유감

출처 : https://namu.wiki/w/%EC%98%A4%EC%A6%88%EB%9E%9C%EB%93%9C


Heap은 프로그래머가 필요에 따라 메모리 공간이 동적 할당/소멸되는 영역입니다.

하지만 스택은 힙과는 달리 정적으로 할당이 되기 때문에 컴파일 시 미리 스택에 공간이 할당이 되어있습니다.


예를 들자면 아래와 같습니다.


0x08048510   <+0>: push   ebp

0x08048511   <+1>: mov    ebp, esp

0x08048513 <+3>: sub    esp, 0x120


위의 어셈 코드와 같이 0x120(288)만큼 할당하는 코드를 볼 수 있듯이 스택은 컴파일할 때에 할당되는 영역입니다.

하지만 힙은 런타임 시 할당되는 유용하게 사용되는 공간입니다.

이 영역은 시작과 동시에 메모리에 올라고 프로그램이 종료될때까지 남아있습니다.


이러한 힙 영역을 사용하기 위해서는 동적할당에 대해 알아두어야됩니다.


info malloc !

흔히 C언어로 코딩 좀 해봤다 싶으면 봤을 듯한 함수인 malloc입니다.

이 malloc 함수는 동적으로 메모리를 할당하는 함수로서 아래와 같이 생겨먹었습니다.


void* malloc(size_t size)


함수를 호출 시 할당하고자 하는 메모리의 크기를 인자에 전달하면 그 크기만큼 메모리를 할당하게 되고 그리고 그 할당한 메모리의 주소를 리턴하게 됩니다.

메모리에 할당에 실패하면 NULL을 리턴하게 됩니다.


Eh? return type is void* ??

ㅗㅜㅑ... 반환 타입이 void*네요.
왜 그럴까요?
이 malloc 함수는 그저 메모리를 할당해주는 함수이기 때문에 이 개발자라는 녀석이 어떤 데이터형으로 할당하는지 알 수 없습니다.
그렇기 때문에 void*로 반환하여 개발자가 입맛에 맞게 변환하여 사용할 수 있게 만들어뒀습니다.

Hell o malloc

malloc은 아래와 같이 생겨먹었습니다.

struct malloc_chunk {
  INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */

  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;

  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  struct malloc_chunk* bk_nextsize;
};

INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */

INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */



할당을 하게 되면 이와 같은 구조를 가지고 있습니다.

prev_size 필드에는 free가 된 이전의 chunk의 사이즈를 가지고 있습니다.


size 필드에는 할당된 사이즈를 담고 있는데.

x86 아키텍처에서는 최소 4바이트 만큼의 공간이 필요합니다.

각 필드들은 8바이트 단위로 정렬이 되서 실제 크기는 더 커질 수 있습니다.


그리고 size 필드에는 3가지의 플래그가 있습니다.

P 플래그는 PREV_INUSE로 이전 chunk가 사용 여부를 나타내는 녀석입니다.

이 플래그가 지워져있으면 free chunk라는 의미가 됩니다.


N 플래그는 NON_MAIN_ARENA로 멀티 쓰레드로 돌아가는 프로그램에서 쓰레드마다 다른 힙 영역을 사용하는 경우

현재 chunk가 main 힙에 속하는지 여부를 나타냅니다.


M 플래그는 IS_MMAPPED로 해당 필드가 mmap()으로 할당된 것인지 아닌지를 나타냅니다.

mmap()으로 할당된 chunk는 malloc과는 다른 방식으로 메모리를 관리합니다.


#define PREV_INUSE       0x1

#define IS_MMAPPED       0x2

#define NON_MAIN_ARENA   0x4


#define SIZE_BITS        (PREV_INUSE|IS_MMAPPED|NON_MAIN_ARENA)

#define chunksize(p)     ((p)->size & ~(SIZE_BITS))


struct malloc_chunk* fd;         /* double links -- used only if free. */

struct malloc_chunk* bk;

struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */

struct malloc_chunk* bk_nextsize;

이 힙이 free될 때에 usable area영역에 fd와 bk가 생성이 되는데
fd는 forward pointer
bk는 backward pointer
를 의미하며 큰 chunk에서는 fd_nextsize와 bk_nextsize가 생성이 됩니다.

이는 더블 링크드 리스트의 prev, next와 비슷하다고 보시면 됩니다.

그럼 이정도로 설명을 하도록 하고 UAF로 넘어가보도록 하겠습니다.

Yeah Use After Free!

동적할당된 힙을 free하고 다시 재사용할 때에 취약점이 발견되는 것을 UAF라고 합니다.
아래의 코드는 UAF 취약점이 존재하는 코드입니다.

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

int main(int argc, char *argv[]){
    void *one, *two;

    one = (char*) malloc(170);
    printf("[0] one : %s\n", (char*) one);

    printf("[1] input string in one : ");
    scanf("%s", (char*) one);

    printf("[2] one : %s\n", (char*) one);
    printf("one pointer : %p\n", (char*) one);
    free(one);

    two = (char*) malloc(170);
    printf("two pointer : %p\n", (char*) two);
    printf("[3] two : %s\n", (char*) two);
    return 0;
}

이 코드를 실행해보면 아래와 같은 결과를 볼 수 있습니다.


one에 입력한 내용이 two에도 출력이 되고 one 주소와 two의 주소가 같음을 볼 수 있습니다.

왜 이럴까요?


Why?

malloc의 caching 때문이다.


출처 : http://g.oswego.edu/dl/html/malloc.html


caching 기능의 Deferred Coalescing이 있는데.

free를 하더라도 chunk를 정리하는 것보다는 같은 사이즈로 요청을 받을 때에 병합또는 분할하는 시간을 절약하고자 재활용을 하게 해주는 것입니다.


출처 : 아는 동생의 중고거래 (무려 어제 있었던 일이다)


disas GDB


초록색 : one size
파란색 : 사용하지 않은 공간 사이즈

gdb로 보게 되면 이렇게 생겨져있습니다.
처음 할당된 부분이기 때문에 fd, bk가 있지 않습니다.

아직 힙에 아무 내용을 넣지 않았기때문에 깨끗합니다.


이제 값이 들어간 것을 볼 수 있습니다.


그리고 free한 뒤에 two를 malloc을 하게 됩니다.

free할 때에 재사용할 힙 공간이 처음에 사용한 0x804b008만 있기 때문에 자연스레 처음에 사용한 힙이 재사용되면서 전에 입력한 값이 출력이 되게 됩니다.



띵똥

Use After Free in sample program

아래는 문제의 코드입니다.


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

typedef struct q1w2e3r4{
    int id;
    char name[20];
    void (*clean) (void *);
} VULNED;

void cleanMem(void *mem){
    free(mem);
}

void hacksure(){
    system("/bin/sh");
}
    
int main(int argc, char *argv[]){
    void *vuln_tst;
    VULNED *vuln = malloc(170);

    fflush(stdin);
    printf("Enter your id number : ");
    scanf("%d",&vuln->id);
    fflush(stdin);
    printf("Enter your name : ");
    scanf("%s", vuln->name);
   
    vuln->clean = cleanMem;

    if(vuln->id > 400){
        printf("Your id is too big!\n");
        vuln->clean(vuln); 
    }                                   
 
    vuln_tst = malloc(170); 
    strcpy(vuln_tst,argv[1]); 
 
    free(vuln_tst);
    vuln->clean(vuln); 

    return 0;
}


문제의 코드이다.
이 코드의 출처는 멋지고 잘생기고 시스템 해킹 잘하는 cd80 문서에서 코드를 참조하였다.

직접 풀어보고 감을 익히는 게 좋을 것 같다.

$ gcc sample.c -o sample -z execstack -fno-stack-protector

컴파일을 하고 문제를 구경해보도록 합시다.


Scenario

먼저 사용을 하고 free를 해야 다시 할당을 할 때에 다시 그 공간을 사용하게 해야됩니다.

vuln의 id 값이 400보다 크면 id가 크다는 문자열을 띄워주면서 cleanMem을 실행하면서 vuln을 free를 해주게 됩니다.

그 다음에 vuln과 같은 크기로 vuln_tst을 할당하고 strcpy로 인자를 vuln_tst에 복사를 한 뒤에 vuln_tst를 free를 해주고 이미 free되있는 vuln을 호출하게 됩니다.


먼저 이에 대해서는 vuln과 vuln_tst은 같은 주소를 가르키고 있고, vuln_tst에 값을 크기 상관없이 조져버릴 수 있습니다.

그렇게 조져버린 메모리를 끝에서 호출을 하기 때문에 우리는 eip 주작질이 가능하게 됩니다.


Exploit

먼저 vuln의 clean이 어디에 위치해있는지를 알아봅니다.



그렇다면 clean은 24만큼 떨어진 곳에 있습니다.



이렇게 이후에 eip를 주작질을 할 수 있음을 볼 수 있습니다.


그 뒤로는 휘리릭 뾰로롱 하면 쉘이 따집니다.



넵, 안녕 !



길고 긴 글을 읽으시느라 수고하셨습니다.

감사합니다.



Reference


Comment



'0x20 Security > 0x21 System' 카테고리의 다른 글

Buffer Overflow (BOF)  (2) 2016.07.22
About LD_PRELOAD  (0) 2016.07.20
Use After Free (UAF)  (1) 2016.07.13
How main() is executed on linux  (2) 2016.07.04
System Hacking :: Memory Protection  (0) 2016.06.28
[SSA] 시스템 해킹 스터디 6주차  (8) 2015.11.09

Comment 1

  • 띠용
    2016.08.23 16:12 신고 수정 답글

    malloc을 처음 할당하면
    fd, bk가 없다고 하셨는데 이유가 뭔가요?