[linux] 1. ASLR(Adress Space Layout Randomization)에 대하여
두비니
·2020. 10. 11. 17:58
ASLR
Address Space Layout Randomization
Linux Memory Protection - 1
1. What is ASLR?
자 오늘은 리눅스 메모리 보호기법 첫 번째 입니다. ASLR에 대해서 배워 볼 건데요, 일단 ASLR이란 Address Space Layout Randomization의 줄임말입니다. 직역하면 "주소 공간 배열 무작위화" 입니다. 말 그대로 주소를 매번 실행할 때마다 무작위화시켜 공격을 방해합니다. 이때 어떤 주소를 랜덤화시키는지가 중요합니다. ASLR은 데이터영역만 무작위화시킵니다. (참고)같은 방법으로 바이너리 영역을 무작위화시키는 보호기법은 PIE입니다. 참고로 데이터 영역은 대표적으로 스택, 힙, 라이브러리 영역을 포함하고, 다른 영역들도 있긴 한데 그건 밑에 실습에서 봅시다.
::장점::
- 직접적인 메모리 참조가 힘들어짐
::단점::
- 주소의 Image Base, 즉 시작 주소만 바뀜
ㄴVA(절대주소) = Image base값 + RVA(상대주소)로 정해지는데, Image base만 바뀌기 때문에 중간에 address leak를 한다면 다른 함수도 결국 접근 가능(3. Exploit Technique의 4번 참고)
2. Training
ASLR같은 경우에는 Linux자체에서 실행하는 메모리 보호기법입니다. 따라서 시스템 자체에서 해제를 시켜주거나, 프로그램을 컴파일할 때 미리 설정시켜줘야 합니다. 먼저 단일 프로그램에 적용시켜보고, 그 다음에 리눅스 전체에 적용시켜 봅시다. 코드도 다 공유하니 웬만하면 다 따라해보는게 좋을 것 같습니당
간단한 코드를 짜서 컴파일해봅시다.
아 ASLR을 끄는거는 root계정에서 해야해서, 모르는 사람들을 위해 링크 첨부합니다.
#include <stdio.h>
#include <dlfcn.h>
/*
compile
$ gcc -m32 -no-pie -o addrtest.c addrtest -lc -ldl
set ASLR OFF
echo 0 > /proc/sys/kernel/randomize_va_space
*/
int a = 10;
int c;
int main()
{
static int b = 20;
static int d;
char *heap = (char *)malloc(100);
int stack;
long binary;
void *handle;
handle = dlopen("/lib/libc.so.6", RTLD_LAZY);
binary = (long)dlsym(handle, "printf");
printf(" ==========[code section]==========\n");
printf(" [*] main() addr : 0x%08x\n",&main);
printf(" ==========[data section]==========\n");
printf(" [*] value a addr : 0x%08x\n",&a);
printf(" [*] value b addr : 0x%08x\n",&b);
printf(" ==========[BSS section]==========\n");
printf(" [*] value c addr : 0x%08x\n",&c);
printf(" [*] value d addr : 0x%08x\n",&d);
printf(" ==========[heap section]==========\n");
printf(" [*] heap addr : 0x%08x\n",heap);
printf(" ==========[stack section]==========\n");
printf(" [*] stack addr : 0x%08x\n",&stack);
printf(" ==========[binary section]=========\n");
printf(" [*] binary addr : 0x%08x\n", binary);
return 0;
}
코드가 라이브러리를 불러오는 코드라 #dlfcn.h에 대한 내용은 처음 볼 수도 있는데 그냥 쭉 읽어보고 아~ 그렇구나 하면 됩니다. 옵션들 뜻은 다음과 같습니다.
- -m32 : 32byte으로 생성
- -no-pie : PIE를 끄는 기능인데, 나중에 배울 것이니 모르면 일단 넘어갑시당
- -o : 실행 파일 생성
- -lc -ldl : 라이브러리 관련 옵션입니다.
컴파일은 사실 몇개의 기법을 더 꺼줘야 하긴 하는데, 최소한만 켜놨습니다.
그리고 ASLR을 설정하는 방법은 리눅스 자체적으로 하는 보호기법이라 터미널에서 해주어야 합니다. ASLR 관련 명령어는 다음과 같습니다.
echo [NUM] > /proc/sys/kernel/randomize_va_space
[NUM]에 대한 내용은 다음과 같습니다.
randomize_va_space = 0 : ASLR 해제
randomize_va_space = 1 : 스택, 라이브러리 랜덤화
randomize_va_space = 2 : 스택, 라이브러리, 힙 랜덤화 (ASLR ON)
다음 결과는 차례로 ASLR을 실행시킨 상태에서 실행했을 때와 껐을 때를 비교한 사진입니다.
보면 ASLR을 실행시키면 code, data, BSS section은 그대로이지만, heap, stack, binary section은 실행때마다 주소가 바뀌는 것을 볼 수 있습니다. 당연히 ASLR을 끈다면 그대로이고요! 간단하죠?
이번에는 전체 리눅스에서 확인해봐서 어떤 영역이 랜덤화하는지 확인해봅시다.
기본 터미널에서 cat /proc/self/maps 를 입력해줍시다. 뜻은 cat에 대해 (cat) 전체 프로세스들 중(proc) 현재 실행 중인(self) 프로세스들의 주소 맵(maps)을 보여줘 라는 뜻입니다. 두 번 입력해 봅시다.
보면 저 빨간 네모를 친 부분이 다른 것을 확인할 수 있습니다. 각 내용을보면 [heap], *.so 파일들, [stack], [vvar], [vdso]파일 영역들의 주소가 바뀌는 것을 볼 수 있습니다. 일단 [heap], [stack]영역은 알 것이고, *.so파일들은 바이너리 파일들입니다. 앞서 설명했듯이 스택, 힙, 라이브러리 파일이 바뀌는건 당연히 볼 수 있습니다. 그리고 [vvar]이랑 [vdso]는 찾아봤는데, [vvar] 은 찾아보니깐 vDSO와 커널 변수 선언이 있는 곳이라고는 하는데... 솔직히 잘 모르겠어요... 마지막으로 [vdso]는 vDSO(virtual Dynamic Shared Object)영역으로, vsyscall영역을 보안상의 문제로 대신하는 영역이라고 합니다.
그리고 바뀌지 않는 부분도 좀 조사해봤습니다.
1. /bin/cat
이건 그 명령어 cat 맞습니다. 이건 GNU Core Utilities; coreutils라고 리눅스에서 기본적으로 필요한 명령어 모음집입니다. 이건 프로그램 내부 실행과는 상관없으니 굳이 주소가 바뀔 필요 없겠죠?
2. [vsyscall]
얘는 그냥 syscall의 overhead문제를 줄이기 위해서 user space에 할당된 커널 영역이라고 합니다. 이게 고정된 주소에 값을 할당하기 때문에 이걸 vDSO가 대신한다고 하네요. 그럼 vsyscall은 왜 있는거지...? 알게 된다면 추가로 서술하도록 하겠습니다.
아래 글은 vsyscall exploit에 대한 글인데, 정말 +a라 궁금한사람만 읽어보시길
그럼 ASLR을 끄고 실행하면 어떻게 될까요?
당연히 이전에는 바뀌었던 부분이 이제는 그대로인걸 알 수 있습니다. (얘도 root안에서 해줘야해요)
음 근데 이거에 대한 결론은 "heap, stack, binary영역이 바뀌는 구나"만 보면 될 것 같아요.
3. Exploit Technique?
1) Bruteforcing
네 멋있는말로 bruteforcing이라고 하고, 전문적인 용어로 노가다라고 하죠. "할 수"는 있지만, 당연히 확률이 굉장히 낮은 편이고, 만약 익스 시도 횟수에 제한이 있는 프로그램이라면 당연히 절대 쓸 수 없는 방법이겠죠?
2) NOP Sled 기법
nop slide라고도 부르는 nop sled기법은 nop를 썰매(sled)타듯이 원하는 코드까지 간다고 붙여진 이름입니다.
우선 nop란 0x90으로, 아무 명령도 실행하지 않는 명령어입니다. 프로그램이 실행 중에 nop를 만난다면, 아무런 명령이 이루어지지 않기 때문에 다음 명령으로 넘어갑니다.
따라서 원하는 명령 앞에 nop를 잔뜩 넣어준다면, 원하는 메모리의 정확한 시작주소를 몰라도, nop sled를 통해 결국은 쉘코드가 실행이 됩니다.
다만 이것도 매번 바뀌는 폭이 크다면 힘들겠죠?
3) Symbolic link + RTL
ASLR을 적용해도 바뀌지 않는 부분이 있습니다.
바로 /bin/cat부분인데, symbolic link를 이용하여 이 영역 안의 항상 있을법한 기계어 (ex \x01)를 파일명으로 사용하고, 이전에 만들었던 /tmp/sh 파일에 대한 실행 명령어(/tmp/sh)를 넣어 우회합니다. 이렇게 하는 이유는 기존의 RTL공격에서는 /tmp/sh를 환경변수에 넣어서 실행했는데, ASLR은 환경변수도 사용할 수 없기 때문에 있을법한 기계어를 넣어서 일종의 동명이인?을 이용하는 느낌입니다.
여기서 이 방법에 대한 실습까지 하기에는 너무 길어지기 때문에 나중에 새로운 글로 올리도록 하겠습니다. 아래는 직접 실습해보고 싶은 사람들을 위해! (perl로 되어있긴 한데 기본적인건 같기때문에 잘 python으로 바꿔서 풀면 됩니다.)
4) Address Leak
사실 현실적인 우회 방법은 이 방법이라고 생각합니다.
ASLR은 각 힙, 스택, 바이너리의 주소가 바뀌는 것은 맞지만, Image Base, 즉 시작 주소만을 바꿔주는 것 뿐입니다. 따라서 절대주소 VA의 값은 Image base값 + RVA(상대주소)로 정해지는데, Image base만 바뀐다는 거죠. 그러면 예를 들어, printf와 system사이의 주소 차를 알아놓고, printf의 주소를 프로그램 실행 중에 알아 낼 수 있다면? 결국 system함수도 실행시킬 수 있겠죠?
그러면 결국 이 주소를 빼낼 수 있으면 끝인데, 사실 이 주소를 알아내는 방법은 정말 다양한 방법들이 존재합니다. Address Leak이나 Memory Leak라고하면 많은 기법들이 나올겁니다. 개인적으로 문제를 풀어봤을 때는 Leak 후 ROP로 문제를 푸는 식의 전개가 가장 많았습니다. 이건 각자 더 찾아보십쇼!
사실 1번과 2번 외에도 단순 반복실행, 시스템 상의 ASLR을 끄고 진행하는 등 다른 방법들도 있지만 단순 확률을 시험하는 기법이나 권한이 없다면 불가능한 방법들을 제외/생략하였습니다. (사실 짱짱맨 ROP쓰면됨ㅎㅅㅎ)
끝! 내일은 카나리 기릿
추가 참고글
bpsecblog.wordpress.com/2016/05/16/memory_protect_linux_1/
(이 밑으로는 영어글들인데 어려워도 내용이 괜찮아서 첨부합니다)
나머지 글들
0. Abstract : dokhakdubini.tistory.com/297
1. ASLR <--- You are here
2. Canary : dokhakdubini.tistory.com/299
3. NX(with DEP) : dokhakdubini.tistory.com/300
4. PIE : dokhakdubini.tistory.com/304
'Coding_Algorithm > Operating System' 카테고리의 다른 글
[linux] 3. NX(with DEP)에 대하여 (0) | 2020.10.13 |
---|---|
[linux] 2. Canary에 대하여 (0) | 2020.10.13 |
[linux] 0. 메모리 보호기법 - abstract (0) | 2020.10.11 |
bits.c code (비공개) (0) | 2020.10.07 |
[linux] scp로 서버 파일 가져오기 (1) | 2020.10.03 |