상세 컨텐츠

본문 제목

[Protection Tech] 메모리 보호기법 정리

SYSTEM HACKING/Exploit Tech

by koharin 2019. 7. 3. 17:38

본문

728x90
반응형

1. NX Bit(MS : DEP)

 

#NX Bit(Never eXecute bit, 실행 방지 비트)

-프로세스 명령이나 코드 또는 데이터 저장을 위한 메모리 영역을 따로 분리하는 CPU 기술

-NX 특성으로 지정된 모든 메모리 구역은 데이터 저장을 위한 용도로만 사용되고, 프로세서 명령어가 그 곳에 상주하지 않음으로써 실행되지 않도록 만듦

 

# DEP(Date Execution Prevention)

-마이크로소프트 윈도우 운영체제에 포함된 보안 기능으로, 악의적인 코드가 실행되는 것을 방지하기 위해 메모리를 추가로 확인하는 하드웨어 밑 소프트웨어 기술

-DEP는 두 가지 모드로 실행됨

--하드웨어 DEP : 메모리에 명시적으로 실행 코드가 포함되어 있는 경우를 제외하고 프로세스의 모든 메모리 위치에서 실행할 수 없도록 표시함

--소프트웨어 DEP : CPU가 하드워어 적용 DEP를 지원

Ex. 공격자가 heap, stack 영역에 shellcode를 저장해서 실행하기 위해서는 해당 영역에 실행권한이 있어야 한다. (shellcodestack이나 heap 영역에 저장할 수 없다.)

DEP가 적용되지 않으면 shellcode가 실행되지만 적용된 경우 실행권한이 없으므로 shellcode 실행 안 됨

(프로그램에서 해당 동작 예외처리 후 프로세스 종료 됨)

 

#BOF 취약점이 존재하는 프로그램에 빌드할 때 스택에 실행권한을 설정하여 컴파일

Ex. $ gcc -z execstack -o DEP-disabled DEP.c (DEP.c라는 파일을 컴파일 시)

    $ gcc -o DEP-enabled DEP.c

 

#checksec.sh에서 출력

$ checksec.sh file DEP-disabled

=> DEP-disabled file: NX disabled

Checksec.sh file DEP-enabled

=> DEP-enabled file: NX enabled

 

# NX 설정여부 확인 방법

binary file

$ readelf -w -l ./DEP-disabled | grep ‘GNU_SACK’ | grep -q ‘RWE’

$ readlef -w -l ./DEP-enabled | grep ‘GNU_STACK’ | grep ‘RWE’

=> readelf 명령어 이용해 파일의 세그먼트 헤더 정보에서 ‘GNU_STACK’Flg 값이 ‘RWE’이면 NX가 활성화되었다고 판단

Process

$ readelf -w -l /proc/<PID>/exe | grep ‘GNU_STACK’

CPU

$ grep nx /proc/cpuinfo

=> /proc/cpuinfo 파일에서 ‘nx’ 문자가 있는지 확인

 

#우회 방법

RTL 공격

 

2. ASLR (Address Space Layout Randomization)

 

# ASLR

-메모리 손상 취약점 공격을 방지하기 위한 기술

-스택, , 라이브러리 등의 주소를 랜덤한 영역에 배치하여 공격에 필요한 target address를 예측하기 힘들게 만듦

-프로그램이 실행될 때마다 주소들이 변경됨

Ex. RTL 공격을 위해서는 공유 라이브러리에 위치한 함수의 주소를 알아야 하는데(system 함수 등),

이러한 주소 값들이 프로그램이 호출될 때마다 변경되어 공격이 어려워짐 (불가능하지는 않음)

 

#ASLR 적용

$ echo [option] > /proc/sys/kernel/randomize_va_space

(ex. echo 0 > /proc/sys/kernel/randomize_va_space)

(option:: 0: ASLR 해제 1: 랜덤 스택 & 랜덤 라이브러리 설정 2: 랜덤 스택 & 랜덤 라이브러리 & 랜덤 힙 설정)

 

#checksec.sh

-바이너리 파일의 보호 기법 확인

System-wide ASLR (kernel.randomize_va_spade): On (Setting: 2)

=> Checksec.sh에서 출력하는 결과

 

#Memory map

/proc/<PID>/maps 파일을 통해 프로세스의 메모리 구조 및 주소 확인 가능

 

# “Checksec.sh” 파일에서 ASLR 설정 여부 확인하는 방법

“/proc/1/status” 파일 내 ‘PaX’ 단어 검색하고 검색 결과에 ‘R’이 존재하는지 확인

$ cat /proc/1/status | grep ‘Pax’

-> “ASLR enabled”로 판단

“/proc/1/status” 파일 내 ‘PaX’ 단어 없을 경우 ‘sysctl’ 명령어 사용

$ sysctl -a | grep ‘kernel.randomize_va_space = ’

-> ‘sysctl’ 명령어로 출력된 내용 중 “kernel.randomize_va_space =”의 값을 통해 ASLR 설정 판단

 

3. Canaries

 

#Canaries/Canary word

-버퍼 오버플로우(BOF)를 모니터하기 위해 버퍼와 제어 데이터 사이에 설정된 값

-BOF 발생 시 Canary 값이 손상되고, Canaries 데이터 검증에 실패하여 BOF 경고 출력, 손상된 데이터 무효화 처리

 

 

 

#Canarie 종류

1> Terminator canaries

-Canary 값을 문자열 끝을 나타내는 문자들을 이용해 생성

-NULL (0x00), CR (0x0d), LF (0x0a), EOF (0xff) 로 구성

Canaries 우회 위해 return address 쓰기 전 NULL 문자 써야 함->NULL 문자로 인해 overflow 방지(strcpy()NULL문자 위치까지 복사)

=> 공격자는 Canary로 알려진 값으로 겹쳐쓰고 정보를 틀린 값들로 제어해 Canary 검사 코드 통과함

2> Ramdom canaries

-Canary 값을 랜덤하게 생성

-프로그램 초기 설정 시 전역 변수에 Canary 값 저장

-> 이 값은 매핑되지 않은 페이지에 저장됨. 해당 메모리 읽으려고 시도할 경우 segmentation fault 발생하고 프로그램 종료

=> 공격자가 Canary 값이 저장된 stack address를 알거나 스택의 값을 읽어올 수 있는 프로그램 있으면 Canary 값 확인 가능

3> Random XOR canaries

-Canary의 값을 모든 제어 데이터 또는 일부를 사용해 XOR-scramble 하여 생성

->Canary의 값, 제어 데이터가 오염되면 Canary의 값이 달라짐

-Random Canaries와 동일한 취약점 가지고, 단지 Canary 값을 Stack에서 읽어오는 방법에 더 복잡, 공격자는 Canary 다시 인코딩 위해 Original Canary , 알고리즘, 제어 데이터 필요

 

#Canary 값 확인 방법

Ex. 정상적으로 종료되는 경우 사용자가 ‘A’*32 입력

사용자 값이 저장되는 영역이 0x7ffffffffe180 일 때, 이 영역에 ‘A’*32가 저장됨

Ex. Canary가 덮히는 경우 사용자가 ‘A’*40 + ‘B’*8 입력

Canary 값이 0x4242424242424242(BBBBBBBB)으로 변경됨

0x400610 코드 영역에서 rax 레지스터에 rbp 0x8 영역에 저장된 값을 저장

Rbp(0x7ffffffffe1b0) 0x8 = 0x7ffffffffe1a8

0x7ffffffffe1a8 영역에 저장된 값 : 0x4242424242424242

0x400614 코드 영역에서 rax 레지스터에 저장된 값과 fs: 0x28 레지스터에 저장된 값을 xor 연산

0x40061d 코드 영역에서 rax 레지스터의 값이 0x61061c8ecf993242 이기 때문에 다음 코드 영역(0x40061f)으로 이동

이로 인해 프로그램에서 Error 메시지를 출력 "stack smashing detected"

 

#binary file에서 보호기법 확인 => checksec.sh

출력 결과

Canary_Do-not-set: No canary found  $ checksec.sh --file ./Canary_Do-not-set

Canary: Canary found  checksec.sh --file ./Canary

 

# “checksec.sh” 파일에서 Canary 설정 여부 확인하는 방법

1> Binary

$ readelf -s ./Canary_Do-not-set | grep __stack_chk_fail

=> readelf 명령어로 해당 파일(Canary_Do-not-set)의 심볼 테이블 가져와 Canary 설정 여부 확인

파일의 심볼 테이블에 “__stack_chk_fail”이 있으면 Canary 적용된 것으로 판단

 

2> Process

/proc/<PID>/exe 파일에 ‘Symbol table’ 정보 있는지 확인

readelf -s /proc/12602/exe |grep '__stack_chk_fail'

 

4. RELRO(RELocation Read Only)

 

#ELF 바이너리/프로세스의 데이터 섹션의 보안을 강화하는 일반적인 기술

메모리가 변경되는 것을 보호하는 기술

바이너리 컴파일 시 FULL_RELRO 옵션 주면 .ctors, .dtors, .jcr, .dynamic, .got 섹션이 읽기전용상태(Read-Only)가 됨.

#Partial RELRO, FULL RELRO

 

* Lazy Binding: 모든 외부 함수 주소를 한 번에 로딩하지 않고, 함수 호출 시점에 해당 함수의 주소만 공유 라이브러리로부터 알아오는 것

-> static 방식으로 컴파일 시 라이브러리의 모든 코드가 포함됨녀 파일 크기가 커지고 라이브러리 내용을 메머리에 매핑시켜야 하는 단점 있지만, lazy binding은 실행 시점에 필요한 함수의 주소만 알아오면 되므로 exe 파일 크기 훨씬 작고 실행속도가 빠름

 

Dynamic Linking 방식으로 컴파일된 ELF 바이너리는 공유 라이브러리 내에 위치한 함수의 주소를 동적으로 알아오기 위해 GOT(Global Offset Table) 사용.

(dynamic link 방식으로 컴파일하면 library가 프로그램 외부에 위치하므로 함수 주소 알아오는 과정이 필요하므로)

 

Ex. PLTGOT 이용해 공유 라이브러리 내 함수 주소 알아오는 과정

puts 함수 호출 시, puts 함수 호출이 처음이면 링커dl_runtime_resolve 함수를 통해 필요한 함수의 주소를 알아오고, GOT에 그 주소를 쓴 후 해당 함수를 호출하는 과정을 거침. 처음이 아닐 경우 GOTputs 함수의 주소가 적혀있으므로 puts 함수의 주소를 알아오는 과정없이 바로 함수를 호출함.

 

Dynamic link 방식으로 프로그램 만들어지면 함수 호출 시 PLT 참조하고, PLTGOT으로 점프. GOTlibrary에 존재하는 실제 함수의 주소 쓰여있어 이 함수를 호출하게 됨.

 

★ GOT Overwrite

단순히 “pwd” 출력하고 종료하는 프로그램이 있을 때, 만약 puts 함수가 system 함수가 될 수 있으면 puts(“pwd”); system(“pwd”); 로 되어 “pwd” 명령을 수행할 수 있음

이때 GOT Overwrite 사용!

puts 함수의 GOTsystem 함수의 주소를 덮으면 puts 함수 호출 시, GOT에 저장되어 있는 puts 함수의 주소가 실제 puts 함수의 주소인 줄 알고 system 함수를 실행하게 됨

=> Partial RELRO인 경우 GOT Overwrite 가능

 

 

5. PIE

바이너리 영역을 랜덤화하는 보호기법

 

 

※보호기법 이해 위한 개념 정리

 

+PLT(Procedure Linkage Table)

외부 프로시저를 연결해주는 테이블. PLT 통해 다른 라이브러리에 있는 프로시저 호출해 사용 가능

 

+GOT(Global Offset Table)

PLT가 참조하는 테이블. 프로시저들의 주소가 들어있음.

 

+링커(Linker)

라이브러리 등 필요한 오브젝트 파일들을 연결시키는 것으로 그 작업을 링킹(linking)이라고 함.

 

Static link는 파일 생성 시 library 내용 포함한 실행파일 생성

Dynamic link는 공유라이브러리를 사용해 라이브러리를 하나의 메모리 공간에 매핑하고 여러 프로그램이 공유하여 사용.

(librarexe 파일이 의존적이라 library 없이 실행 불가)

 

Ex. Printf 함수를 호출하는 코드가 있을 때, include한 헤더파일에는 printf의 선언이 있음.

소스파일을 실행파일로 만들기 위해서는 컴파일(compile) 과정 거쳐야 하는데 컴파일 통해 object 파일이 생성됨.

하지만 obj 파일은 printf 구현 코드를 모르므로 그 자체로 실행 가능하지 않음.

따라서 printf 실행 코드(printf 구현 코드를 컴파일한 obj 파일.

이런 obj 파일이 모여있는 곳이 library)를 찾아서 obj 파일과 연결시켜야 함.

728x90
반응형

관련글 더보기