본문 바로가기

전공 공부/리눅스 보안 실습

버퍼 오버 플로우 공격하기, 소스코드 분석하기 - RedHat 6.2

728x90

버퍼 오버 플로우?

https://ko.wikipedia.org/wiki/%EB%B2%84%ED%8D%BC_%EC%98%A4%EB%B2%84%ED%94%8C%EB%A1%9C

 

버퍼 오버플로 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 다른 뜻에 대해서는 오버플로 문서를 참조하십시오. 버퍼 오버플로(영어: buffer overflow) 또는 버퍼 오버런(buffer overrun)은 메모리를 다루는 데에 오류가 발생하여 잘못된 동작을 하는 프로그램 취약점이다. 컴퓨터 보안과 프로그래밍에서는 프로세스가 데이터를 버퍼에 저장할 때 프로그래머가 지정한 곳 바깥에 저장하는 것을 의미한다. 벗어난 데이터는 인접 메모리를 덮어 쓰게 되며 이때 다른 데이터가 포함되

ko.wikipedia.org

https://epicarts.tistory.com/38?category=816044

 

Redhat 6.2 설치 - 버퍼 오버플로우 테스트용 서버

버퍼 오버플로우 테스트를 위해 redhat-6.2-i386.iso를 설치하겠습니다. ftp://archive.download.redhat.com/pub/redhat/linux/6.2/en/iso/i386/redhat-6.2-i386.iso 위 링크에서 다운 받아 주시면 됩니다. 두번..

epicarts.tistory.com

버퍼 오버 플로우 테스트를 위해 Redhat 6.2를 먼저 설치해 줍니다.

 

버퍼 오버플로우 실습 코드 : https://github.com/epicarts/buffer-overflow

실습 코드는 깃허브에 올려놓았습니다.

 

 

현재에는 DEP 기법, ASLR 기법과 같이 메모리를 보호하는 보안 기법 때문에 테스트를 하려면 보안이 취약한 서버를 사용해서 해야 합니다. 그렇기 때문에 테스트는 Redhat 6.2에서 진행하였습니다.

 

DEP(Data Execution Protection) 기법기법

DEP은 실행 권한이 없는 메모리 영역의 코드가 실행되지 못하도록 방지하는 기법.

 

ASLR(Address Space Layout Randomization) 기법기법

ASLR은 프로세스의 가상 주소가 어떤 공간에 맵핑될 때 그 위치를 프로그램 실행 시마다 랜덤 하게 변경하는 보안 기법..

 

 

위 소스코드를 Rehat에 옮겨 줍니다.

 

gcc 명령어를 이용해 컴파일하고자 하는 buffer-overflow.c 파일을 바이너리 파일로 만들어 줍니다.

-g 옵션을 주어 바이너리 파일에 디버깅 정보를 삽입해 줍니다.

 

 

objdump -D buffer-overflow

objdump -D 명령어를 이용해 buffer-overflow 파일을 디스 어셈블을 할 수 있습니다.

 

 

 

objdump -D buffer-overflow | grep -A2 hack

grep 옵션을 이용해 바이너리 파일에서 hack이라는 패턴 매칭 라인이 된 라인을 찾고 -A2 옵션을 이용해 이후 2개의 라인을 출력해 주었습니다.

hack 함수의 주소를 보면 08 04 86 14입니다. 기억해줍시다.

 

 

`python -c 'print "A" *23'`

파이썬을 이용하면 손쉽게 내가 원하는 문자열을 커맨드로 넘겨줄 수 있습니다. 이를 이용해 overflow를 발생시킬 문자열을 입력하겠습니다.

 

 

./buffer-overflow `python -c 'print "A" *23'`

buffer-overflow 실행시킬 때 인자로 A를 23개 전달하였습니다.

 

이제 소스코드와 비교하여 어떻게 이런 결과가 나왔는지 파헤쳐 보겠습니다.

 

 

이중 아스키코드로 변환한 메모리 저장 은 표현이 가능하면 아스키 값으로 표현하고 그렇지 않으면 전부 . 으로 보여줍니다.

 

빨간색 네모 위부터 보면, char 형으로 buf라는 변수의 크기를 20바이트(char= 1byte = 8bit) 만큼 할당해 주었습니다.

이렇게 buf라는 변수를 만들면 아무 의미 없는 쓰레기 값이 들어가게 되고 이를 dumpcode 함수에 넣어보겠습니다.

dumpcode함수는 현재 메모리 주소(32bit), 메모리에 저장되어 있는 값, 아스키코드로 변환한 메모리 저장 값 이렇게 3개의 정보를 보여줍니다.

 

1byte = 8 bit = 11111111 (2진수) = 0xFF (16진수)
unsigned char = 0 ~ 255 == 0x00 ~ 0xFF (dumpcode에서 부호 없는 char로 받음)

메모리 주소는 32bit 이므로 2의 32승 총 0x000000 ~ 0xffffff 까지(0 ~ 4,294,967,295개)의 메모리 주소를 가질 수 있습니다. 

 

0xbffffcf4 에 저장된 값: 54

0xbffffcf5 에 저장된 값: fd

0xbffffcf6 에 저장된 값: ff

''''

0xbffffcf03 에 저장된 값: 08

0xbffffd04 에 저장된 값: c3

 

이 중 0xbffffcf4라는 메모리 주소에 54라는 값이 들어 있는 것을 확인할 수 있습니다.

이때 메모리 주소1씩 증가하고 있는 것을 확인 할 수 있습니다. 주소 하나에는 1byte(8bit)의 값을 저장할 수 있는데, unsigned char는 1byte로 0~255까지의 값을 가질 수 있습니다. 그렇기 때문에 1byte 단위로 증가를 시켜 표시를 해야 입력한 문자를 볼 수 있습니다.

 

만약 4byte를 차지하는 int를 선언했다면  -2,147,483,648~2,147,483,647 까지의 값을 표현 가능하고, 메모리 주소에 4바이트만큼 크기를 차지하고 있을 것이므로, 리틀 엔디안 방식의 메모리 저장 방식이면, 반대 방향으로 4바이트를 1바이트씩 가져온 다음 10진수로 확인을 해야 정수 값을 볼 수 있을 겁니다.

 

이제 strcpy로 저장한 값을 buf에 넣은 뒤 출력을 해보겠습니다.

같은 메모리 주소인 0xbffffcf4 에서 부터 총 23바이트가 0x41(아스키코드: A)로 채워진 것을 볼 수 있습니다. buf에 담을 수 있는 값은 20바이트가 한계이지만 초과되어 메모리에 쓰였습니다..!!

이게 바로 값이 넘치는 overflow(오버플로우)라고 할 수 있습니다.

 

 

원래대로라면 위처럼 0x41(아스키코드:A)이 20바이트까지 들어가는 것이 정상적인 동작일 겁니다. 

그 뒤에 오는 00 fd ff df는 프레임 포인터고, cb 09 03 40은 복귀 주소입니다.

 

프레임포인터는 이제 복귀할 메모리 주소를 가지고 있는 프레임 포인터와 함께 복귀 주소를 강제로 오버플로우(overflow)를 이용해 덮어 씌움으로써 강제로 hack함수로 이동시키게 할 것입니다.

 

이를 위해서 기존 buf 크기인 20byte에 4byte만큼 추가로 아무 값이나 침범하게 만든 다음 복귀 주소에 hack주소의 값을 씌우면 가능할 것입니다.

 

 

 

 

./buffer-overflow `python -c 'print "A" *24 + "\x14\x86\x04\x08"'` 

이제 오버 플로우 발생을 위해 뒤에 인자 값을 더 추가시켜 오버플로우를 발생시켰습니다. 

 

hack의 함수 주소를 보면 08 04 86 14인데 리틀 엔디안 방식으로 메모리에 쓰기 때문에 바이트 단위반대 방향로 저장해줘야 됩니다. 그렇기 때문에 오버 플로우의 취약점을 이용해 함수 호출을 하기 위해서는 위와 같은 방식으로 메모리에 입력해 줘야 정상적으로(?) 복귀 주소 대신에 hack 함수가 호출이 됩니다.

 

 

 

hack() 함수를 호출하는 부분은 소스코드 어디에도 없지만 오버플로우를 발생시켜 강제로 함수를 호출하게 만들었기 때문에 Hacking success!라는 메시지가 뜨게 됩니다.

 

이 오버플로우가 실행되게 된 이유는 strcpy에서 문자열 길이를 확인하지 않고 복사했기 때문에 가능한 일입니다. 만약 strcpy대신에 strncpy를 사용했다면 오버플로우가 발생하지 않았을 것입니다.

 

또 버퍼오버플로우를 방지하기 위한 메모리 보안 기법이 적용되지 않았기 때문에 함수 주소가 고정되어 있어서 실행될 수 있었습니다.

728x90