brunch

You can make anything
by writing

C.S.Lewis

by Younggi Seo May 03. 2018

Section4: malicious RCE attack

The injection of the buffer overflow #2

일단 우분투 리눅스 시스템(낮은 버전, 최근의 버전은 이미 많이 공격에 대해 패치되어 있다)에서 버퍼오버플로우 공격을 예방한 설정 파일을 해제해야 한다. 하나는,

  ~$ sudo vi /proc/sys/kernel/randomize_va_space 의 명령을 통해 값을 0으로 변경한다.

randomize_va_space 의 값이 1이나 2로 되어 있으면 목표 시스템에 ASLR*이 켜진 상태이다. 기본적으로 우분투에서는 난수화 로직이 켜 있지만 공격이 이루어지려면 이 기능을 꺼두어야 한다.



그리고 앞서 C로 작성한 버퍼 오버플로우 테스트를 위한 소스 코드를 컴파일할 때,

 ~$ gcc -g -fno-stack-protector -z execstack -o overflowtest overflowtest.c

GNU 컴파일러 모음이 GCC(GNU Compiler Collection) 명령어를 통해 위와 같이 컴파일을 할 때, '-fno-stack-protector' 플래그를 써서 GCC 스택 보호 메커니즘을 꺼둬야 한다. 그리고 '-z execstack' 옵션은 데이터 실행 금지(data execution prevention, DEP)라고 특정 메모리 영역을 실행 불가능하도록 설정하기 때문에 버퍼 오버플로우 공격을 예방하게 한다.**



자, 이제 본격적으로 오버플로우의 진행과정에 대해 살펴보자. 일단 위와 같이 컴파일한 다음 아래 명령어와 같이 'AAAA'를 커맨드 라인 인자로 하여 프로그램을 실행한다.

~$ ./overflowtest AAAA
Executed Normally



그러면 위와 같이 정상적으로 실행했다는 메시지 결과를 확인할 수 있다. A를 다섯 개가 아닌 네 개를 입력했는데(실제 변수 선언 시 char buffer[5]로 했었다), 문자열의 맨 끝자리에는 빈 글자(null byte)가 들어가기 때문이다. 기술적으로 볼 때 A를 다섯 개 쓰면 비록 한 자리이긴 해도 이미 버퍼가 넘치게 된다. 그래서 만일 overflowtest에 뜻밖의 입력 값을 먹이면 강제로 버퍼가 넘치도록 할 수도 있다(Crash라고도 부름).



이제 다음과 같이 A 문자열을 길게 해서 프로그램을 호출해보면,

~$ ./overflowtest AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault


비정상 종료(Crash)된다. 이 프로그램의 문제점은 function에서 strcpy 함수를 써서 구현한 데서 비롯한다. strcpy 함수는 문자열을 다른 곳에 복사하지만 제공된 인자가 목적으로 하는 문자열 변수에 꼭 맞는지 경계를 확인하지 않는다(확인하는 함수는 strncpy). 그렇게 때문에 strcpy 함수는 다섯 글자만 담는 목적지 변수에 세 개, 다섯 개, 심지어 수백 개의 글자를 복사하려는 시도가 가능하다. 네 글자를 담을 수 있는 문자열에 다섯 글자 이상을 복사하면 나머지의 문자는 스택의 인접한 메모리 주소를 덮어쓴다(overflowed).



따라서 잠재적으로 function의 스택 프레임의 나머지와 더 높은 영역의 메모리(그림 1.에서 아래쪽의 스택 프레임까지)를 덮어쓴다. 그렇다면 스택 프레임 기저 바로 다음에 있는 메모리 주소(RETurn address)를 기억할 수 있겠는가? 프레임 전의 값(호출값, Fucntion 함수의 Return value)이 스택에 푸시(push)되기 전에, main 함수는 fucntion이 리턴했을 때 계속해서 실행할 주소를 푸시한다. 그런데 buffer 변수에 복사할 문자열이 충분히 길다면 buffer로부터 EBP까지 계속해서 메모리를 덮어쓰게 되어 리턴할 주소는 물론이고 심지어 main 함수의 스택 프레임까지 덮어쓰게 된다.

 


그림1.


strcpy 함수가 overflowtest의 첫 번째 인자를 buffer 변수로 위치시킨 다음 function은 main 함수로 되돌아간다. 스택 프레임이 스택에서 팝(pop)하면 팝한 값(EBP의 위치)을 받은 CPU는 리턴 주소의 메모리(EBP) 위치에 있는 명령을 실행하려고 시도한다. 그런데 앞서 말했듯이 그림 1.과 같이 리턴 주소에 'A'를 여러 번 써서 덮어썼기 때문에 CPU는 메모리 주소 41414141(A를 네 번 쓴 문자열의 16진수 표현)에 있는 명령어를 실행하려고 한다.



하지만 프로그램이 메모리의 아무 영역이나 읽고 쓰고 실행하면 극단적인 혼란이 발생하므로 그렇게 하지 못한다. 여기서 메모리 영역 41414141은 프로그램 영역 밖이므로 앞부분에서 설명한 것과 같은 세그멘테이션 폴트가 나면서 프로그램이 죽는다. 이때부터 '악의적인 의도'가 개입할 차례이다. 어셈블리어와 디버깅을 통한 중단점 설정에 맞춰 스택 프레임의 메모리(레지스터) 라벨을 확인해가면서 overflowed() 함수가 개입해야 할 포인트의 라벨(스택 메모리 주소)을 추출해야 한다.







* ASLR(Address Space Layout Randomization)은 억지로 번역한 용어로 '주소 공간 설계 난수화' 기법.

** DEP 기법은 특정 메모리 영역(EIP 레지스터)을 실행 불가능하게 하는 것으로 스택에 셸 코드를 채운 다음 EIP가 가리켜서 실행하는 것으로부터 예방하여 오버플로우를 방지함.

 https://unix.stackexchange.com/questions/66802/disable-stack-protection-on-ubuntu-for-buffer-overflow-without-c-compiler-flags 여기를 참조하면 정보보안 기사 10회 실기 작업형 문제로 출제된 버퍼 오버플로우의 대응법 중 Canary 기법Non-executable stacks(DEP 기법)를 리눅스로 설정하는 방법에 대해 알 수 있다. ASLR 해제 명령어(버퍼 오버플로우 공격을 실습하기 위한)의 다른 방법도 추가적으로 알 수 있다(echo 0 > /proc/sys/kernel/randomize_va_space)




매거진의 이전글 Section2: malicious RCE attack
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari