Hubeen's Home

Memory Protection Techniques

writen by hubeen




Linux 환경에서의 메모리 보호 기법에 대해 작성하려고 합니다.

/*이 글은 틀린 정보가 있을 수도 있으며 글의 맥락이 축구공처럼 뻥뻥 돌아다닐 수도 있습니다.*/


일단 메모리 보호라는 개념을 알아보도록 하겠습니다.



네, 그렇습니다.

운영체제에서 실행되고 있는 프로세스에게 메모리를 할당을 해주게 되는데.

할당되지 않은 영역의 메모리에 접근을 하는 것을 막기 위해 있는 것이 메모리 보호 기법의 주된 목적입니다.


이러한 메모리 보호 기법들은 짱짱한 분들이 해커들의 공격을 막기 위해 열심히 노력을 해서 완성이 된 녀석들입니다. 




1. ASLR (Address Space Layout Randomization)



이 ASLR은 2001년 즈음에 Linux PaX 프로젝트에서 처음으로 "ASLR" 개념을 설계하고 2002년부터 커널 스택 무작위 배치를 구현을 하였다고 합니다.

( https://en.wikipedia.org/wiki/Address_space_layout_randomization )


이 녀석은 말 그대로 주소 공간을 랜덤한다. 입니다.


흔히 Buffer Overflow 공격으로부터 보호하기 위해 Stack, Heap, Libc, 프로세스 주소 공간을 포함하는 데이터 영역 위치들을 랜덤하게 변경하여 메모리의 특정 악용 함수 주소를 랜덤하게 하여 공격을 방지를 할 수 있게 하는 녀석입니다.


실제로 이 ASLR이 작동을 하는지 눈으로 확인을 하도록 하겠습니다.


cat /proc/self/maps 명령을 입력을 하여 직접 확인해보도록 하세요! 



앙 바뀐띄~


이 ASLR이 적용이 되있는지 안되있는지 확인하는 방법은 아래와 같습니다.


cat /proc/sys/kernel/randomize_va_space 


0 : No randomization. Everything is static.

1 : Conservative randomization. Shared libraries, stack, mmap(), VDSO and heap are randomized.

2 : Full randomization. In addition to elements listed in the previous point, memory managed through brk() is also randomized.


그렇다면 이 ASLR을 해제를 해보도록 하겠습니다.


echo 0 > /proc/sys/kernel/randomize_va_space


보시는 것과 같이 주소 값이 바뀌지 않는 것을 볼 수 있습니다.


#include <stdio.h>

#include <stdlib.h>


int main()

{

char *heap = NULL;


heap = (char*) malloc(50);


printf("[Heap Address] : %p\n", heap);


return 0;

}


간단한 코드로 주소를 출력을 해보도록 하겠습니다.

gcc -o a a.c



왼쪽은 ASLR을 끈 상태이며 오른쪽은 ASLR이 적용되있는 상황입니다.


보시는 것과 같이 ASLR이 적용되있지 않을 때에는 힙의 주소 값이 일정하지만 ASLR이 켜져있을 경우에는 주소 값이 랜덤하게 바뀌는 것을 볼 수 있습니다.




2. DEP (Data Execution Prevention)



DEP라는 녀석에 대해 알아보도록 하겠습니다.



네, 그렇습니다!

흔히 Overflow로 인해 메모리 영역에 훼손이 발생하더라도, Data 영역에서 실행(Execution)을 방지함으로써 공격을 방어하는 기법입니다.


고로 해커가 Buffer Overflow 공격으로 ret을 스택영역에 놓은 쉘코드 주소로 변경했을 때에 DEP가 적용이 안되있는 경우에는 그대로 쉘코드가 실행이 되어 쉘을 딸 수 있겠지만, DEP가 적용이 되어있다면 실행 권한이 없으므로 쉘코드가 실행되지 않고 종료가 됩니다.


아래는 포스트 할 때 사용한 예제 코드 입니다.


#include <stdio.h>

#include <string.h>

#include <sys/types.h>

#include <unistd.h>


pid_t getpid(void);

pid_t getppid(void);


/*

        The Lord of the BOF : The Fellowship of the BOF

        - gremlin

        - simple BOF

*/


int main(int argc, char *argv[])

{

    char buffer[256];

    pid_t pid;

    char cmd[25];

    if(argc <2){

        printf("argv error\n");

        exit(0);

    }

    strcpy(buffer, argv[1]);

    printf("%s\n", buffer);


    if ((pid=fork()) == -1){

        printf("fork failed\n");

    }

    else if(pid != 0){

        printf("getpid : %ld\n", getpid());

        sprintf(cmd, "cat /proc/%d/maps", getpid());

        system(cmd);

    }


}


이 예제 코드는 간단한 bof 취약점이 존재하는 프로그램입니다.

코드가 익숙하시다고요?

넵, 맞습니다.

LOB gate 문제 코드거든요!


그렇다면 컴파일을 하고 보게 되면?



보시는 것과 같이 rw-p 로 스택에 실행 권한이 없는 것을 볼 수 있습니다.


그리고 DEP 해제를 하고 보도록 하겠습니다.


gcc 컴파일을 하실 때에 dep를 해제를 하시려면 -z execstack 옵션을 넘겨주시면 됩니다.


DEP 해제 : gcc -z execstack a.c -o a



역시 보시는 것과 같이 rwxp로 스택에 실행권한이 있는 것을 볼 수 있습니다. 

이렇게 바이너리에 DEP가 걸려 있지 않는다면 bof를 통해 ret을 쉘코드가 있는 스택 주소로 변조하면 쉘코드가 잘 실행이 되겠죠?




3. ASCII-Armor



다음으로는 ASCII_Armor 보호 기법에 대해 알아보도록 하겠습니다.

이 녀석은 뭘 하는 녀석일까요?


이름 그대로 "아스키 철갑옷" 이름 값하는 녀석입니다.

라이브러리를 공유 라이브러리 영역에 올리지 않고 텍스트 영역의 16MB 아래의 주소에 할당하는 녀석입니다.


이 ASCII-Armor가 적용이 되게 되면 라이브러리 영역 주소의 최상위 1byte가 \x00으로 됩니다.



보시는 것과 같이 최상위 1byte가 \x00 으로 되잇는 것을 볼 수 있습니다.


이렇게 \x00 은 NULL로 인식이 되고 RTL같은 체이닝 공격을 할 수 없게 되어 익스플로잇을 하지 못하게 만드는 녀석입니다.




4. SSP (Stack Smashing Protector)



마지막으로 SSP입니다.

이 Stack Smashing Protector 는 gcc 4.1버전 부터 지원하는 Buffer Overflow 방어 기법입니다.

4.1 버전 이상인 gcc에서 아래와 같은 코드를 컴파일 한 뒤에 BOF를 발생시켜보겠습니다.


#include <stdio.h>

#include <string.h>


int main(int argc, char *argv[]){

        char buffer[5];


        strcpy(buffer, "0123456789abcdef");

        return 0;

}


컴파일 하고 실행을 하게 되면 다음과 같이 *** stack smashing detected ***: ./b terminated 라는 문자열이 나타나는 것을 볼 수 있습니다.



gdb로 까보도록 해보겠습니다.



초록색 박스 안에서는 카나리 값을 스택에 저장하는 부분이며,

주황색 박스 안에서는 카나리 값이 변조되었는지 안되있는지 체크하는 루틴입니다.



esp+0x1c에 카나리가 들어간다고 합니다.


그렇다면 아마도 0x450b8200이 카나리 값이겠네요 (웃음)



어이쿠 카나리 값을 "5"로 덮어버리고 말았어요!


이 부분에서 카나리 값이 없어지고 카나리 체크하는 루틴에서 걸려서 보호를 하게 됩니다.


이 카나리는 버퍼와 sfp의 사이에 숨어있어요!


buf[5] | dummy[?] | canary[4] | sfp[4] | ret[4]


이 SSP를 해제하는 옵션은 아래와 같아요.


SSP 해제 : gcc b.c -o b -fno-stack-protector




길고 멍청한 글을 읽어주셔서 감사합니다.




Comment


댓글 0