Mitigation: NX & ASLR
# 들어가며
- r2s를 통한 공격자의 침입을 더 어렵게 하려면
- 공격자가 메모리에서 임의 버퍼의 주소를 알기 어렵게 하고
- 메모리 영역에서 불필요한 실행 권한을 제거하는 보호 기법을 추가로 도입해야 함
# ASLR
- Address Space Layout Randomization(ASLR): 바이너리가 실행될 마다 스택, 힙, 공유 라이브러리 등을 임의의 주소에 할당하는 보호 기법
- ASLR은 커널에서 지원하는 보호 기법이며, 다음의 명령어로 확인 가능
- 리눅스에서 이 값은 0, 1, 또는 2의 값을 가질 수 있음
- 각 ASLR이 적용되는 메모리 영역은 다음과 같음
- No ASLR(0): ASLR을 적용하지 않음
- Conservative Randomization(1): 스택, 힙, 라이브러리, vdso 등
- Conservative Randomization + brk(2): (1)의 영역과 brk로 할당한 영역
- Figure 1. ASLR 예제 코드
// Name: addr.c
// Compile: gcc addr.c -o addr -ldl -no-pie -fno-PIE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
char buf_stack[0x10]; // 스택 버퍼
char *buf_heap = (char *)malloc(0x10); // 힙 버퍼
printf("buf_stack addr: %p\n", buf_stack);
printf("buf_heap addr: %p\n", buf_heap);
printf("libc_base addr: %p\n",
*(void **)dlopen("libc.so.6", RTLD_LAZY)); // 라이브러리 주소
printf("printf addr: %p\n",
dlsym(dlopen("libc.so.6", RTLD_LAZY),
"printf")); // 라이브러리 함수의 주소
printf("main addr: %p\n", main); // 코드 영역의 함수 주소
}
- addr.c: 메모리의 주소를 출력하는 코드
- gcc로 컴파일하고 실행해보면 다음과 같은 결과를 확인할 수 있음
- 스택 영역의 buf_stack, 힙 영역의 buf_heap, 라이브러리 함수 printf, 코드 영역의 함수 main, 그리고 라이브러리 매핑 주소 libc_base가 출력됨
- 코드 영역의 main 함수를 제외한 다른 영역의 주소들은 실행할 때마다 변경됨 → 실행할 때 마다 주소가 변경되기 때문에 바이너리를 실행하기 전에 해당 영역들의 주소를 예측할 수 없음
- 바이너리를 반복해서 실행해도 printf 주소의 하위 12비트 값은 변경되지 않음
- 리눅스는 ASLR이 적용되었을 때, 파일을 페이지(page) 단위로 임의 주소에 매핑함
- 따라서 페이지 크기인 12비트 이하로는 주소가 변경되지 않음
- libc_base와 printf의 주소 차이는 항상 같음
- ALSR이 적용되면, 라이브러리는 임의 주소에 매핑됨
- 그러나 라이브러리 파일을 그대로 매핑하는 것이므로 매핑된 주소로부터 라이브버리의 다른 심볼들 까지의 거리(offset)는 항상 같음
# NX
- No-eXecute(NX)는 실행에 사용되는 메모리 영역과 쓰기 영역에 사용되는 메모리 영역을 분리하는 보호 기법
- 어떤 메모리 영역에 대해 쓰기 권한과 실행 권한이 함께 있으면 시스템이 취약해지기 쉬움
- 코드 영역에 쓰기 권한이 있으면 공격자는 코드를 수정하여 원하는 코드가 실행되게 할 수 있고, 반대로 스택이나 데이터 영역에 쓰기 권한이 있으면 Return to Shellcode와 같은 공격을 시도할 수 있음
- CPU가 NX를 지원하면 컴파일러 옵션을 통해 바이너리에 NX를 적용할 수 있으며, NX가 적용된 바이너리는 실행될 때 각 메모리 영역에 필요한 권한만을 부여받음
- gdb의 vmmap으로 NX 적용 전후의 메모리 맵을 비교하면, 다음과 같이 NX가 적용된 바이너리에는 코드 영역 외에 실행 권한이 없는 것을 확인할 수 있음
- 반면, NX가 적용되지 않은 바이너리에는 스택, 힙, 데이터 영역에 실행 권한이 존재하는 것을 확인할 수 있음
- Checksec을 이용한 NX 확인
- checksec을 이용하면 다음과 같이 바이너리에 NX가 적용됐는지 확인할 수 있음
- Return to Shellcode w/t NX
- 이전에 실습한 Return to Shellcode의 예제인 r2s에 NX 보호기법을 적용한 후, 동일한 익스플로잇을 실행했을 때의 결과를 볼 것
- 우선 checksec로 확인하면 NX가 활성화되어 있음
- 이 바이너리를 대상으로 사용했던 익스플로잇 코드를 실행하면, segmentation fault가 발생하게 되는데 그 이유는NX가 적용되어 스택 영역에 실행 권한이 사라지게 되면서 쉘코드가 실행되지 못하고 종료되기 때문임
💡 NX의 다양한 명칭
- NX를 인텔은 XD(eXecute Disable), AMD는 NX, 윈도우는 DEP(Data Execution Prevention), ARM에서는 XN(eXecute Never)라고 칭함
- 명칭만 다를 뿐 모두 비슷한 보호 기법
# 마치며
- NX와 ASLR이 적용되면 스택, 힙, 데이터 영역에는 실행 권한이 제거되며, 이들이 할당되는 주소가 계속 변함
- Address Space Layout Randomization(ASLR): 메모리 무작위 주소에 할당하는 보호 기법으로 최신 커널들은 대부분 적용되어 있음. 리눅스에서는 페이지 단위로 할당이 이루어지므로 하위 12비트는 변하지 않는다는 특징이 있음
- NX(No-eXecute bit): 프로세스의 각 세그먼트에 필요한 권한만 부여하는 보호 기법으로 일반적으로 코드 영역에는 읽기와 실행을, 나머지 영역에는 읽기와 쓰기 권한이 부여됨
- but, 바이너리의 코드가 존재하는 영역은 여전히 실행권한이 존재하며, 할당되는 주소도 고정되어 있음
- 코드 영역에는 유용한 코드 가젯들과 함수가 포함되어 있음
- 반환 주소를 쉘 코드로 직접 덮는 대신, 이들을 활용하면 NX와 ASLR을 우회하여 공격할 수 있음
- RTL
- ROP
'Hacking Tech > System Hacking' 카테고리의 다른 글
[Dreamhack] Background: RELRO (0) | 2022.09.14 |
---|---|
[Dreamhack] Background: Library - Static Link vs. Dynamic Link (0) | 2022.09.07 |
[Dreamhack] Mitigation: Stack Canary (0) | 2022.09.04 |
[Dreamhack] Memory Corruption: Stack Buffer Overflow (0) | 2022.09.01 |
[Dreamhack] Background: Calling Convention (0) | 2022.08.31 |