실행 가능 공간 보호

실행 가능 공간 보호()는 기계어가 특정한 메모리 영역에서 실행될 시에 예외를 일으키게 하는 목적으로, 메모리 공간을 실행 불가능하게 마킹하는 것이다. 이것은 NX 비트 같은 하드웨어 특징 또는 어떤 경우에는 이러한 기능을 하는 소프트웨어 에뮬레이션을 이용한다. 그러나 에뮬레이팅이나 NX 비트의 제공은 어느 정도의 오버헤드를 일으킬 수 있다; 하드웨어에서 제공되는 NX 비트는 주목할 만한 오버헤드를 일으키지 않는다.

Burroughs 5000은 1961년에 도입되었을 때 하드웨어 지원의 실행 가능 공간 보호를 제공하였다; 이 기능은 적어도 2006년까지 후임자에 의해 계승되었다. 태그 방식의 구현에서 메모리의 각 워드는 (코드나 데이터를 지정하면서) 연관되고 숨겨진 태그 비트를 갖는다. 그래서 사용자 프로그램들은 프로그램 워드에 쓰거나 심지어 읽지도 못하며, 데이터 워드는 실행될 수 없게 된다.

만약 운영 체제가 메모리의 쓰기 가능한 영역의 일부나 전부를 실행 불가능하게 마크할 수 있다면, 스택이나 힙 메모리 영역이 실행되는 것을 방지할 수 있다. 이것은 특정한 버퍼 오버플로 취약점(특히 실행 가능한 코드를 주입하는)을 방어할 수 있다. 이러한 공격들은 특정한 메모리(보통 스택)가 쓰고 실행 가능하다는 것에 의존한다; 만약 그렇지 않다면 공격은 실패할 것이다.

 OS 구현 
많은 운영 체제들이 실행 가능 공간 보호를 구현하거나 정책을 가지고 있다. 다음은 알파벳 순서로 이러한 시스템을 목록화한 것이다.

아키텍처 독립적인 에뮬레이션을 제공하는 기술은 하드웨어 지원이 없는 프로세서에서도 사용 가능하다.

 안드로이드 
안드로이드 2.3 이후부터, 기본으로 실행 불가 스택과 힙을 포함하는 실행 불가 페이지들을 지원한다."Memory Management Security Enhancements", Android Security Overview, retrieved 2012/07/29."Android code change forcing NX", Android Source Repository Change, retrieved 2011/07/14."Android Compatibility Requirement for NX", Android Code Review , retrieved 2011/07/14.

 리눅스 
리눅스 커널은 x86-64와 IA-32 프로세서에서 NX 비트를 지원한다. 이러한 특징들은 커널 버전 2.6.8 이후부터 리눅스 커널에 포함되었다.

2비트 x86 커널은 일반적으로 NX 비트를 예상하지 않았기 때문에, 32비트 x86 커널에서의 NX 비트 사용은 주목할만 하다; NX 활성화 패치는 이러한 커널에서의 NX 비트의 사용을 보증한다.

페도라, 우분투 그리고 오픈수세 같은 몇몇 데스크탑 리눅스 배포판들은 기본 커널에서 기본 옵션으로 (32비트 모드에서 NX 비트에의 접근 획득을 요구하는) HIGHMEM64를 활성화시키지 않았는데, 이것은 PAE 모드가 펜티엄 프로 이전과 셀러론 그리고 펜티엄 M에서(NX 지원이 없는) 부팅 실패를 유발할 수 있기 때문이다. 페도라 코어 6와 우분투 9.10 이후에서는 PAE와 NX를 지원하는 커널-PAE 패키지를 제공한다.

NX 메모리 보호는 지원하는 하드웨어를 갖는 모든 우분투에서 사용 가능 했었다. 우분투 9.10 이후의 32비트 PAE 데스크톱 커널 또한 NX CPU 특징을 갖는 하드웨어를 필요로 한다. NX 하드웨어가 존재하지 않는 시스템들에서 32비트 커널은 소프트웨어 에뮬레이션을 통해 NX CPU 특징을 어느 정도 제공한다.

실행 불가 기능 또한 x86이 아닌 다른 프로세서들에 존재한다.

 Exec Shield 
Exec Shield라는 이름의 레드햇 커널 패치는 32비트 x86 CPU들에서 NX 기능을 활용하기 위해 개발되었다. Exec Shield는에뮬레이션의 복잡한 부분을 다루기 위해 코어 코드의 변경을 요구하기 때문에 기본 커널에 병합되는 것이 거부되었었다. 이것의 레거시 CPU 지원은 상위 코드 세그먼트 제한을 추적함으로써 NX 에뮬레이션과 비슷한 결과를 만든다. 이것은 문맥 전환에 오직 몇 사이클의 오버헤드만 부과한다. NX 비트가 없는 레거시 CPU에서 Exec Shield는 코드 세그먼트 제한 아래의 페이지들에 대한 보호는 실패한다; 스택 같은 상위 메모리 실행 가능 영역을 마크하는 mprotect() 호출은 또한 제한 아래의 실행 가능 영역을 모두 마크할 수 있다. 그래서 이러한 경우에 Exec Shield의 계획은 실패한다. 이것은 Exec Shield의 작은 오버헤드 비용 때문이다. 이것은 스택이나 힙이 실행 가능할 필요가 있는지를 지시하는 두 ELF 헤더 마킹을 검사한다. 이것들은 각각 PT_GNU_STACK, PT_GNU_HEAP이다. Exec Shield는 이러한 제어들을 바이너리와 라이브러리 모두에서 설정되게 한다; 만약 실행 파일이 주어진 제한 완화를 요구하는 라이브러리를 로드하면, 실행 파일은 그 마킹을 상속하고 제한을 완화할 것이다.

 PaX 
PaX NX 기술은 NX 기능이나 하드웨어 NX 비트의 사용을 에뮬레이트 할 수 있다. PaX는 32비트 x86 같은 NX 비트를 갖지 않는 x86 CPU에서 동작한다. 리눅스 커널은 PaX를 포함하지 않아서 직접 패치할 수 밖에 없다.

PaX는 두 방식의NX 비트 에뮬레이션을 제공하는데, SEGMEXEC와 PAGEEXEC이 그것이다. SEGMEXEC 방식은 작은 오버헤드가 부과되며 일반적으로 1%미만이다. 이것은 실행과 데이터 접근 사이의 분리에 사용되는 가상 메모리 미러링 때문에 발생한다. SEGMEXEC 또한 작업이 보통 보다 적은 메모리 접근을 허용하게 하는, 작업의 가상 주소 공간을 이등분하는 효과를 갖는다. 이것은 작업이 보통 주소 공간의 반 이상을 요구하기 전까지는 문제가 되지 않으며, 이런 일은 거의 일어나지 않는다. SEGMEXEC는 프로그램들이 더 많은 시스템 메모리(즉 RAM)를 사용하게 하지 않아서, 단지 그들이 얼마나 접근할 수 있는지만을 제한한다. 32비트 CPU에서, 이것은 1.5 GB가 된다.

PaX는 PAGEEXEC에서 속도를 높이기 위해 Exec Shield와 비슷한 방식을 제공한다. 그러나 높은 메모리가 실행 가능하게 마크되었을 때 이 방식은 보호를 잃는다. 이러한 경우에 PaX는 CS 제한 아래 페이지를 보호하기 위한 PAGEEXEC(특정한 메모리 접근 패턴에서 더 높은 오버헤드 동작이 될 수 있는)에 의해 사용되는 오래된 이전 방식의 변수-오버헤드 방식을 사용한다. PAGEEXEC 방식이 하드웨어 NX 비트를 제공하는 CPU에서 사용될 때, 하드웨어 NX 비트가 사용되어서 주목할 만한 오버헤드가 일어나지는 않는다.

PaX는 프로그램이 메모리를 마킹하는 것(잠재적인 익스플로잇에 유용한 방식으로)을 막는 mprotect() 제한을 제공한다. 이 정책은 특정한 애플리케이션의 기능을 막기도 하지만 감염된 프로그램을 비활성화할 수도 있다.

PaX는 아래에 나오는 각 실행 가능 바이너리를 위한 기술들의 기능들에 대한 각각의 제어를 허용한다.
 PAGEEXEC
 SEGMEXEC
 mprotect() 제한
 Trampoline 에뮬레이션
 난수화 실행 가능 베이스
 난수화 mmap() 베이스
PaX는 PT_GNU_STACK와 PT_GNU_HEAP를 무시한다. 과거에 PaX는 이러한 셋팅들을 고려하는 설정이 있었지만, 보안 상의 이유로 옵션이 제거되었다. PT_GNU_STACK의 같은 결과들도 일반적으로 mprotect() 제한을 비활성화함으로써 이루어질 수 있다(프로그램이 로드된 스택을 mprotect()할 것이다). 이것은 항상 참인 것은 아니다; 이것이 실패하는 경우 간단하게 PAGEEXEC와 SEGMEXEC를 비활성화하는 것은 효과적으로 모든 실행 가능 공간 제한들을 제거할 것이다.

 윈도우 
윈도우 XP 서비스 팩 2 (2004)와 윈도우 서버 2003 서비스 팩 1 (2005)를 시작으로, NX 특징들이 x86 아키텍처에서 처음으로 구현되었다. 윈도우에서의 실행 가능 공간 보호는 "데이터 실행 방지"(DEP)라고 불린다.

윈도우 XP 또는 서버 2003 하에서 NX 보호는 중요한 윈도우 서비스에서 배타적으로 기본 옵션으로 사용되었다. 만약 x86 프로세서가 이 특징을 지원한다면 NX 특징은 기본적으로 자동으로 설정된다.

주소 공간 배치 난수화(ASLR) 없이 제공된 초기의 DEP는 잠재적인 return-to-libc 공격을 허용하였고 공격 동안 쉽게 DEP를 비활성화 할 수 있었다. PaX 문서는 왜 ASLR이 필요한지를 상술하였다;http://pax.grsecurity.net/docs/aslr.txt ASLR가 없는 환경에서 DEP에 가능한 개념 증명이 만들어졌다. 이것은 오염된 이미지 같은 준비된 데이터의 주소가 공격자에게 알려질 수 있다는 것이다.

마이크로소프트는 윈도우 비스타와 윈도우 서버 2008에 ASLR 기능을 추가하였다. 이 플랫폼에서는 DEP는 PAE 커널의 자동화된 사용을 통해 구현되었다. 윈도우 비스타 DEP는 메모리의 특정한 영역을 오직 데이터만 갖게 의도함으로써 동작한다. 이로써 NX 또는 XD 비트가 프로세서를 활성화하고 실행 불가하다고 알려진다. 비스타 이후의 윈도우에서 DEP가 활성화되었든지 말든지 간에 특정한 프로세스는 윈도우 작업 관리자의 프로세스 탭에서 볼 수 있다.

윈도우는 마이크로소프트의 "Safe Structured Exception Handling"(SafeSEH)을 통해 소프트웨어 DEP(NX 비트의 사용 없이)를 구현하였다. 적절하게 컴파일된 애플리케이션에서 프로그램 실행 중에 예외가 일어나면 SafeSEH는 예외 처리기가 원본에서 컴파일 된 것인지를 검사한다. 이 보호의 효과는 공격자가 자신의 예외 핸들러(데이터 페이지에 저장하거나 검사되지 않은 프로그램 입력을 통한)를 추가하지 못하게 하는 것이다.

NX가 지원되면, 기본값으로 활성화 된다. 윈도우는 프로그램이 API나 PE 파일의 섹션 헤더를 통해 어떤 페이지를 실행 불가하게 하는 것을 허용한다. API에서 NX 비트에 대한 런타임 접근은 Win32 API 호출인 VirtualAlloc[Ex]와 VirtualProtect[Ex]를 통해 가능하다. 각페이지는 각각 실행 가능이거나 불가로 플래그될 수 있다. 이전의 x86 하드웨어 지원의 부족에도 불구하고, 실행 가능 그리고 불가 페이지 셋팅들 모두 시작 시에 제공된다. NX 이전의 CPU는 'executable' 속성이 영향을 미치지 않는다. 이것은 대부분의 프로그래머들이 적절하게 사용할 수 있게 문서화되어 있다. PE 파일 포맷에서, 각 섹션은 자신의 실행 가능함을 명시할 수 있다. 실행 플래그가 존재해서 시작 시에 표준 링커가 NX 비트 전에 이것을 사용한다. 이것 때문에 윈도우는 NX 비트를 오래된 프로그램에 강제할 수 있다.

 한계 
코드가 작성되고 런타임 시에 실행되는 곳에서 - JIT 컴파일 같은 - 컴파일러는 잠재적으로 익스플로잇 코드를 만드는데 사용될 수 있다(예를 들면 JIT 스프레이를 통해)..Writing JIT-Spray Shellcode for fun and profit , Alexey Sintsov, (pdf) "interesting way to bypass DEP and ASLR in browsers (not only) and Just-In-Time compilers was presented."

 같이 보기 
 버퍼 오버플로
 포맷 스트링 버그
 힙 오버플로
 스택 버퍼 오버플로
 스택 스매싱 보호

 각주 

분류:운영체제 보안