Home NX Bit [MS : DEP]
Post
Cancel

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

Untitled

활성화

gcc -o DEP-enabled DEP.c

Untitled

메모리 권한 확인

  • 다음과 같이 메모리 맵에서 메모리 영역별 설정된 권한을 확인할 수 있습니다.
    • 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-disabledgrep ‘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-enabledgrep ‘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

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//exe |grep 'GNU_STACK'**

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
This post is licensed under GNU AGPL by the author.