/* 
written by kaspy (kaspyx@gmail.com)
*/ 

 

 컴퓨터 등장이래, 지난 30여년동안 해킹 및 보안 기술의 발달로 요즘에는 버퍼오버플로우 및 포맷트링 등의 해킹을 실험할수 있는 환경에 많은 어려움이 있습니다. 그래서 요즘버전의 리눅스 환경에 실제 버퍼 오버플로 같은 공격을 테스트 하기 위해선 몇가지 환경 설정이 필요합니다.

시스템 해킹에 대비한 리눅스의 메카니즘이 무엇이 있을까요??
우선 알려저있는 대표적인 버퍼오버플로우(이하 BOF) 및 포맷스트링 버그를 들수있습니다.
이두가지의 공격기법은 임의의 입력으로 인해 프로그램의 실행 흐름을 바꾸는 것을 기본으로 하는데, 이공격기법은 운영체제의 기본설정으로인해 막혀있습니다.
아래와 같이 간단한 프로그램을 짜서 컴파일 해봅시다.

1. 스택 실행권한 해제 (Non-executable Stack)

프로그램이 실행되면 일종의 메모리 레이아웃을 가지게되는데, 각각의 메모리는 용도에 따라 스택(Stack), 힙(Heap). 텍스트 섹션(.text) 등등으로 구성 되어져 있습니다. 프로세스는 보통 스택 및 힙에 데이터를 저장하여 처리하는데 공격자는 이곳에 임의의 코드를 입력한뒤, 실행흐름을 스택 및 힙의 주소로 변경하여 해킹을 하는 방법을 사용합니다. 그래서 보통 컴파일 할때나 운영체제 단에서 스택에 실행권한을 해제하는 메카니즘을 사용하고 있습니다.
한번 확인해볼까요??
cat /proc/self/maps
명령어를 처보세요.

메모리는 세가지의 속성값을 가지는데, 읽기, 쓰기, 실행 (rwx)의 권한을 가지게 됩니다.
위에서 보는바와 같이 [stack] 및 [heap] 메모리 영역은 rw-p 으로  실행(x) 권한이 없습니다. (여기서 'p' 는 보호메모리라고 가상메모리를 사용하기위한 메카니즘입니다.)
스택에 실행권한을 주기위해서는 컴파일할때 -z execstack 옵션을 추가적으로 주면 됩니다.
아래와같이 간단한 소스코드로부터 컴파일을 해서 확인을 해보겠습니다.

  1. #include <stdio.h>
  2. int main(){
  3.         char buf[8];
  4.         printf("Hello world\n");
  5.         gets(buf);
  6.         return 0;
  7. }
  8. /* compile: gcc -o bof bof.c -z execstack


cat /proc/PID/maps 명령어로 확인해본 결과
위의 첫번째 그림은 -z execstack을 넣지 않고 컴파일 했을때, 두번째 그림은 -z execstack 옵션을 주었을 때입니다. 아래그림에는 스택에 실행권한이 있군요!!

2. 랜덤 스택 (ASLR)

 랜덤스택이란, 프로그램이 실핼 될때마다, 프로세스 내부에 함수 주소 및 스택, 힙의 주소가 랜덤하게 바뀌는 것입니다. 스택실행권한해제(Non-Executable Stack) 메카니즘을 우회하기 위해서 리턴투라이브러리(Return-to-Library) 기법을 사용하는데요. 실행흐름을 이제는 함수주소로 변경하는 기법입니다.

ldd 명령어로 라이브러리 베이스 주소를 확인해보겠습니다.

보시는 바와같이 라이브러리의 주소가 실행될때마다 다른 주소값을 가지고 있습니다.  이를 해제 하기위해선 
sysctl -w kernel.randomize_va_space=0
명령어를 사용해서 해제합니다. (루트권한 필요)

(추가설명으로)
stack만 랜덤하기위해선 
sysctl -w kernel.randomize_va_space=1
heap/stack 둘다랜덤
sysctl -w kernel.randomize_va_space=2

sysctl -w kernel.randomize_va_space=0 을 실행후에 다시 확인해보겠습니다.


확인해보니, 다시 실행되도 라이브러리의 주소가 고정되어 있군요. 이는 스택이나, 힙 메모리 주소도 동일합니다.

- 추가적으로 PIE(Position Independent Executable)라는 동적 랜덤 함수메모리 기법이있긴한데, 다른 챕터에서 다루도록하겠습니다.

3. 스택 가드(Stack Guard)

스택 가드란, 스택 쉴드라고도 하는데 RET 값을 변조하려면 그아래에 있는 EBP와 그아래의 값을 반드시 덮어 써야한다는 것을 기반으로 만들어진 메카니즘 입니다. EBP와 지역 변수내부사이에 일종의 더미값을 가진 Canary을 두어서 이값이 변조되면 BOF로 간주하고 프로그램을 종료해버립니다.


 소스코드는 컴파일 할때, gets() 와같은 BOF 취약점이 있는 함수가 존재하면 스택가드 루틴이 자동으로 포함되어 컴파일 됩니다. 확인해볼까요? 
gcc -o bof bof.c 
명령어로 다시 컴파일후에 확인해보세요.

gets() 함수 밑에 stack값을 체크해주는 추가적인 코드가 들어가 있군요!!
이를 해제하는 컴파일 옵션은 
-fno-stack-protector
을주고 컴파일 하면 됩니다. 또한 스택의 Dummy 값또한 고정해줘야 합니다.
(왜냐하면 버퍼로부터 몇바이트를 덮어쓰는 그런 계산이 어려워지기 때문입니다.)
-mpreferred-stack-boundary=2
아래의 명령어로 컴파일을 해줍니다.
gcc -o bof bof.c -fno-stack-protector -mpreferred-stack-boundary=2


gets() 함수 밑에 스택을 체크해주는 루틴이 사라졌군요!!

4. 정리하기

아래의 소스코드를 
sudo sysctl -w kernel.randomize_va_space=1
gcc -o bof_test bof_test.c -fno-stack-protector -mpreferred-stack-boundary=2 -z execstack
순서로 명령을 실행하여 컴파일해서 실행해보시기 바랍니다. 새로운 쉘이 실행되면 시스템 해킹을 직접 테스트해볼수 있는 환경이 만들어진것입니다. (랜덤 스택같은 경우는 ldd로 직접 확인해보세요.)

  1. void main()
  2. {
  3.         int *ret;
  4.         ret = (int *)&ret+2;
  5.         char buf[] =
  6.         "\xeb\x0b"
  7.         "\x31\xc0"
  8.         "\x31\xd2"
  9.         "\x31\xc9"
  10.         "\x5b"
  11.         "\xb0\x0b"
  12.         "\xcd\x80"
  13.         "\xe8\xf0\xff\xff\xff"
  14.         "/bin/sh\x0";
  15.         (*ret) = (int)buf;
  16.         printf("%d\n",sizeof(buf));
  17. }


* 공격환경 만들 때 컴파일 옵션들
-fno-stack-protector : 스택프로텍트
-fno-builtin : 표준 라이브러리와 링크되지 말고 단독으로 링크하라는 의미
-mpreferred-stack-boundary=2 : 더미 없애기
-z execstack : 스택에 실행 권한 주기

* 랜덤스택 (0 이면 해제)
sysctl -w kernel.randomize_va_space=1
(stack만 랜덤)
sysctl -w kernel.randomize_va_space=2
(heap/stack 둘다랜덤)

* 스택 실행권한
sysctl -w kernel.exec-shield=0 (스택 실행권한 해제)
sysctl -w kernel.exec-shield=1 (위와 반대)

/etc/sysctl.conf에 저장되어있음

* 참고사이트
http://jjoon.net/tc/192?category=0

* 관련 링크

- 버퍼오버플로우(Buffer Overflow) 해킹기법이란??


신고
Posted by 캐스피


티스토리 툴바