NX Bit 보호기법 이론 및 실습
List of Contents
NX Bit [MS : DEP]
NX Bit(Never eXecute bit)란?
- 프로세스 명령어나 코드 또는 데이터 저장을 위한 메모리 영역을 따로 분리하는 CPU의 기술입니다.
- NX 특성으로 지정된 모든 메모리 구역은 데이터 저장을 위해서만 사용되며, 프로세서 명령어가 실행되지 않도록 만들어 준다.
DEP(Data Execution Prevention)이란?
- 마이크로소프트 윈도우 운영 체제에 보안 기능이며, 악의적인 코드가 실행되는 것을 방지하기 위해 메모리를 추가로 확인하는 하드웨어 및 소프트웨어 기술입니다.
DEP는 두가지 종류가 있습니다.
하드웨어 DEP
- 메모리에 명시적으로 실행 코드가 포함되어 있는 경우를 제외하고 프로세스의 모든 메모리 위치에서 실행할 수 없도록 합니다(대부분의 최신 프로세서는 하드웨어 적용 DEP를 지원합니다).
소프트웨어 DEP
- CPU가 하드웨어 DEP를 지원하지 않을 경우 사용합니다.
공격예시
예를 들어 공격자가 Heap, Stack 영역에 Shellcode를 저장해서 실행하기 위해서는 해당 영역에 실행권한이 있어야 합니다.
- DEP가 적용되지 않았을 경우에는 쉘코드가 실행됩니다.
- DEP가 적용된 경우에는 실행권한이 없으므로 쉘코드가 실행되지 않습니다.
- 이런 상황에는 프로그램에서 해당 동작에 대한 예외처리 후 프로세스가 종료가 됩니다.
Example program
- bof 취약점이 존재하는 프로그램을 빌드할 때 스택에 실행권한을 설정하여 컴파일 합니다.
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>
int main(){
char str[256];
char *chare = (char*)malloc(100);
printf("Input: ");
gets(str);
printf("%p\n", str);
}
바이너리 파일의 보호기법 확인
checksec.sh
- checksec을 사용하면 다음과 같은 결과를 출력합니다.
비활성화
gcc -z execstack -o DEP-disabled DEP.c
활성화
gcc -o DEP-enabled DEP.c
메모리 권한 확인
- 다음과 같이 메모리 맵에서 메모리 영역별 설정된 권한을 확인할 수 있습니다.
- DEP disabled의 경우 실행권한(–x-)을 가지고 있는 영역은 17곳입니다.
- DEP enabled의 경우 실행권한(–x-)을 가지고 있는 영역은 5곳입니다.
DEP disabled
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
❯ cat /proc/1024/maps
55844fe3a000-55844fe3b000 r--p 00000000 08:20 17817 /home/sung/mungso/playground/nx/DEP-disabled
55844fe3b000-55844fe3c000 r-xp 00001000 08:20 17817 /home/sung/mungso/playground/nx/DEP-disabled
55844fe3c000-55844fe3d000 r--p 00002000 08:20 17817 /home/sung/mungso/playground/nx/DEP-disabled
55844fe3d000-55844fe3e000 r--p 00002000 08:20 17817 /home/sung/mungso/playground/nx/DEP-disabled
55844fe3e000-55844fe3f000 rw-p 00003000 08:20 17817 /home/sung/mungso/playground/nx/DEP-disabled
55844ff5b000-55844ff7c000 rw-p 00000000 00:00 0 [heap]
7fd358d05000-7fd358d27000 r--p 00000000 08:20 43951 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fd358d27000-7fd358e9f000 r-xp 00022000 08:20 43951 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fd358e9f000-7fd358eed000 r--p 0019a000 08:20 43951 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fd358eed000-7fd358ef1000 r--p 001e7000 08:20 43951 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fd358ef1000-7fd358ef3000 rw-p 001eb000 08:20 43951 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7fd358ef3000-7fd358ef9000 rw-p 00000000 00:00 0
7fd358f04000-7fd358f05000 r--p 00000000 08:20 43947 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fd358f05000-7fd358f28000 r-xp 00001000 08:20 43947 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fd358f28000-7fd358f30000 r--p 00024000 08:20 43947 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fd358f31000-7fd358f32000 r--p 0002c000 08:20 43947 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fd358f32000-7fd358f33000 rw-p 0002d000 08:20 43947 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7fd358f33000-7fd358f34000 rw-p 00000000 00:00 0
7fff318ab000-7fff318cc000 rwxp 00000000 00:00 0 [stack]
7fff318dd000-7fff318e1000 r--p 00000000 00:00 0 [vvar]
7fff318e1000-7fff318e3000 r-xp 00000000 00:00 0 [vdso]
DEP enabled
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
❯ cat /proc/1150/maps
561e8d205000-561e8d206000 r--p 00000000 08:20 19923 /home/sung/mungso/playground/nx/DEP-enabled
561e8d206000-561e8d207000 r-xp 00001000 08:20 19923 /home/sung/mungso/playground/nx/DEP-enabled
561e8d207000-561e8d208000 r--p 00002000 08:20 19923 /home/sung/mungso/playground/nx/DEP-enabled
561e8d208000-561e8d209000 r--p 00002000 08:20 19923 /home/sung/mungso/playground/nx/DEP-enabled
561e8d209000-561e8d20a000 rw-p 00003000 08:20 19923 /home/sung/mungso/playground/nx/DEP-enabled
561e8e914000-561e8e935000 rw-p 00000000 00:00 0 [heap]
7f14076bc000-7f14076de000 r--p 00000000 08:20 43951 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f14076de000-7f1407856000 r-xp 00022000 08:20 43951 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f1407856000-7f14078a4000 r--p 0019a000 08:20 43951 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f14078a4000-7f14078a8000 r--p 001e7000 08:20 43951 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f14078a8000-7f14078aa000 rw-p 001eb000 08:20 43951 /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f14078aa000-7f14078b0000 rw-p 00000000 00:00 0
7f14078bb000-7f14078bc000 r--p 00000000 08:20 43947 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f14078bc000-7f14078df000 r-xp 00001000 08:20 43947 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f14078df000-7f14078e7000 r--p 00024000 08:20 43947 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f14078e8000-7f14078e9000 r--p 0002c000 08:20 43947 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f14078e9000-7f14078ea000 rw-p 0002d000 08:20 43947 /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f14078ea000-7f14078eb000 rw-p 00000000 00:00 0
7ffc7fb86000-7ffc7fba7000 rw-p 00000000 00:00 0 [stack]
7ffc7fbdf000-7ffc7fbe3000 r--p 00000000 00:00 0 [vvar]
7ffc7fbe3000-7ffc7fbe5000 r-xp 00000000 00:00 0 [vdso]
checksec이 NX를 탐지하는 방법
바이너리
- 다음과 같은 방법으로 NX 설정을 확인합니다.
- readelf 명령어를 이용해 파일의 세그먼트 헤더 정보에서 NX 여부를 확인합니다.
- 파일의 세그먼트 헤더 정보에서 ‘GNU_STACK’의 Flg 값이 ‘RWE’라면 NX가 활성화되었다고 판단합니다.
Checksec.sh 163번째 라인
1
2
3
4
5
6
# check for NX support
if readelf -W -l $1 2>/dev/null | grep 'GNU_STACK' | grep -q 'RWE'; then
echo -n -e '\033[31mNX disabled\033[m '
else
echo -n -e '\033[32mNX enabled \033[m '
fi
- NX가 적용된 바이너리의 Flg 값이 ‘RW’ 입니다.
- NX가 적용되지 않은 바이너리의 Flg 값이 ‘RWE’ 입니다.
**readelf -W -l ./DEP-disabled | grep ‘GNU_STACK’ | grep ‘RWE’** |
1
2
3
4
5
❯ readelf -W -l ./DEP-disabled |grep 'GNU_STACK'
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
❯ readelf -W -l ./DEP-disabled |grep 'GNU_STACK' | grep 'RWE'
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
**readelf -W -l ./DEP-enabled | grep ‘GNU_STACK’ | grep ‘RWE’** |
1
2
3
4
5
❯ readelf -W -l ./DEP-enabled |grep 'GNU_STACK'
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
❯ readelf -W -l ./DEP-enabled |grep 'GNU_STACK' | grep 'RW'
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
프로세스
- 다음과 같은 방법으로 실행중인 프로세서의 NX 설정을 확인합니다.
- 바이너리의 확인방식과 동일하며, 전달되는 파일의 경로가 다음과 같이 다릅니다.
- Ex) /proc/
/exe
- Ex) /proc/
- 바이너리의 확인방식과 동일하며, 전달되는 파일의 경로가 다음과 같이 다릅니다.
Checksec.sh 249번째 라인
1
2
3
4
5
6
# fallback check for NX support
elif readelf -W -l $1/exe 2>/dev/null | grep 'GNU_STACK' | grep -q 'RWE'; then
echo -n -e '\033[31mNX disabled\033[m '
else
echo -n -e '\033[32mNX enabled \033[m '
fi
**readelf -W -l /proc/
1
2
3
4
5
6
7
❯ ps -ef | grep DEP
sung 1375 9 0 19:36 pts/0 00:00:00 ./DEP-disabled
sung 1428 9 0 19:37 pts/0 00:00:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox DEP
❯ readelf -W -l /proc/1375/exe | grep 'GNU_STACK'
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
❯ readelf -W -l /proc/1375/exe |grep 'GNU_STACK' | grep 'RWE'
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
CPU
- 다음과 같은 방법으로 CPU의 NX 설정을 확인합니다.
- “/proc/cpuinfo” 파일에서 ‘nx’ 문자가 있는지 확인합니다.
Checksec.sh - line 324
1
2
3
4
5
6
7
8
# check cpu nx flag
nxcheck() {
if grep -q nx /proc/cpuinfo; then
echo -n -e '\033[32mYes\033[m\n\n'
else
echo -n -e '\033[31mNo\033[m\n\n'
fi
}
grep nx /proc/cpuinfo
1
2
❯ grep nx /proc/cpuinfo
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon rep_good nopl xtopology cpuid pni pclmulqdq ssse3 fma cx16 pdcm pcid sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap clflushopt xsaveopt xsavec xgetbv1 xsaves md_clear flush_l1d arch_capabilities