본문 바로가기
CS & Reversing

[정보보안] 버퍼 오버 플로우(BOF) 개념, 원리, 대응방안

by m_.9m 2022. 3. 6.

 

1) 개념

버퍼 오버 플로우는 프로세스가 사용 가능한 메모리 공간을 초과해서 발생되는 공격으로 보안 취약점이다.

C나 C++을 사용할 때 메모리 공간에 제한을 두지 않는 API를 사용해서 발생하는 공격이다.

프로세스가 사용하는 메모리 공간은 Stack, Heap, Text, Data로 나누어져 있다.

 

API(Application Programming Interface 애플리케이션 프로그래밍 인터페이스, 응용 프로그램 프로그래밍 인터페이스)는 컴퓨터나 컴퓨터 프로그램 사이의 연결이다. 일종의 소프트웨어 인터페이스이며 다른 종류의 소프트웨어에 서비스를 제공한다.

 

 

​2) 메모리의 구조

https://kyu9341.github.io/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C/2020/10/04/OS_Process_Structure/

 

상위 메모리 주소

문자열(argv) - 포인터(argv) - argv - 스택영역(stack) - 할당 - 힙영역(Heap) - 데이터(.bss) - 데이터(.data) - 코드(.text)

하위 메모리 주소

 

(1) 스택영역(stack)

 

main(int argc, char argv //인자값)

{

int x; // 지역 변수

sum(); //복귀 주소

}

 

- 프로그램 함수 내에서 사용하는 지역 변수가 저장된다.

- 함수를 호출하는 경우 되돌아오는 변수인 복귀주소를 가지고 있다,

- 함수의 인자값을 가지고 있다.

 

** 프로그램이 자동으로 사용하는 메모리 영역으로 함수 호출과 관계되는 지역변수와 매개변수가 저장된다. 함수 호출시 생성되며 함수가 끝나면 반환된다. stack 사이즈는 각 프로세스마다 할당되지만 프로세스가 메모리에 로드될 때  Stack 사이즈가 고정되어 있어 런타임 시 사이즈를 바꿀 수 없다. 명령 실행 시 자동으로 증가 또는 감소하기 때문에 보통 메모리의 마지막 번지를 지정한다.

 

 

(2) 힙 영역(Heap)

- 프로그램 실행 중 메모리를 동적으로 할당하는 경우 힙 영역에 할당된다.

int * i = (int*) malloc (sizeof(int));

- malloc() 함수를 사용해서 동적으로 메모리를 할당한다.

- int *i 변수 선언은 integer(정수형) 변수의 주소를 저장할 수 있는 포인터 변수를 선언 한 것이다.

- 동적으로 메모리를 할당할 떄 sizeof(int)를 사용하면 4가 리턴 되므로 4바이트의 크기가 동적으로 할당되는 것이다.

- 마지막으로 malloc 함수는 void *를 리턴하는데 int * 는 그것을 int * 형으로 변환해서 맞추어 주는 것이다. 이렇게 동적 메모리 할당 함수를 사용해서 메모리를 할당하면 힙 영역에 할당된다.

- 또한 동적으로 할당된 메모리는 free() 함수를 사용해서 해제해야한다.

 

** 필요에 의해 메모리를 동적으로 할당할 떄 사용하는 메모리 영역으로 동적 메모리 영역이라고 부른다. C에서 mallox() calloc() 등의 함수를 사용하여 메모리 크기를 할당할 수 있으며 메모리 주소 값에 의해서만 참조되고 사용되는 영역이다.

** 위의 Stack과 Heap은 같은 공간을 공유하여 heap이 메모리의 낮은 주소부터 할당되면 Stack은 높은 주소부터 할당되는 식이다. 그래서 각 영역이 상대 공간을 침범하는 일이 발생할 수 있는데 이를 각각 Stack overflow, Heap overflow 라고 한다.

 

 

(3) 데이터(Data) 영역

 

main()

int x; //전역 변수 선언

static int x; //정적 변수 선언

 

- 전역변수를 선언하는 경우 전역변수가 저장된다.

- 정적변수를 선언하면 정적변수가 저장된다.

- 데이터 영역에 변수가 선언되면 자동으로 초기화된다.,

 

**프로그램이 실행될 때 생성되고 프로그램이 종료되면 시스템에 반환되며, 전역번수, 정적변수, 배열, 구조체 등이 저장된다. Data 영역은 다시 BSS , Data(GVAR) 영역으로 나뉘는데 초기화된 데이터는 Data 영역에 저장되고 초기화되지 않는 데이터는 BSS 영역에 저장된다.

**영역을 구분하는 이유

초기화 된 데이터는 초기 값을 저장해야 하므로 Data 영역에 저장되어 rom에 저장된다. 하지만 초기화 되지 않은 데이터까지 rom에 저장되면 큰 size의 rom이 필요하므로 구분한다. (초기화 되지 않은 데이터는 ram에 저장)

 

 

(4) 텍스트(Text) 영역

 

- 읽기만 가능한 메모리 영역이다.

- 프로그램 코드가 저장된다.

** 실행 명령을 포함하는 코드들이 들어가는 부분이다. 프로그램을 시작할 때 컴파일한 프로그램(기계어)이 저장되어 있고 읽기 전용 영역이기에 프로세스가 함부로 변경할 수 없고 변경 시 오류를 발생시킨다. 코드 자체를 구성하는 메모리 영역으로 Hex, Bin 파일 메모리이다. 명령이 위치해 기계어로 제어된다.

 

 

3) BOF 의 종류

 

(1) Stack BOF

 

https://isc9511.tistory.com/119

 

스택에 저장되어 있는 복귀주소가 지역변수에 의해서 침범당하는 공격이다.

** 스택 구조 상, 할당된 버퍼들이 정의된 버퍼의 한계치를 넘는 경우 복귀주소를 변경하여 공격자가 입의의 코드를 수행.

 

 

(2) Heap BOF

 

힙 영역은 동적으로 할당되는 공간이 저장되어 있다. 힙 영역은 하위 주소에서 상위주소로 메모리를 할당한다. 그러므로 경계값을 검사하지 않고 메모리를 사용하면 경계를 초과하는 취약점이 발생한다.

** 힙 구조 상 최초 정의된 힙의 메모리 사이즈를 초과하는 문자열이 힙의 버퍼에 할당될 시, 공격자가 데이터 변경 및 함수주소 변경으로 임의의 코드를 수행.

** 버퍼 오버런(buffer Overrun)

메모리 공간에 할당된 공간보다 더 큰 데이터를 입력하는 프로그램의 오류를 유발할 수 있다. 즉, 공격자는 프로그램의 오류를 유벌하여 시스템을 장악하거나 Shellcode를 복사하여 악성코드를 실행한다.  

 

4) 취약 함수

-취약 함수

strcpy, strcat, getwd, gets, fscanf, scanf, sprintf

-권고 함수

strncat, strncpy, fgets, vfscanf, snprintf, vsnprintf

 

5) 대응 방법

 

(1) ASLR(주소 공간 배치 난수화)

메모리 공격을 방어하기 위해 주소 공간 배치를 난수화하는 기법

공격자가 특정 주소를 불러오는 것을 방지하여 악성 코드 실행을 차단

스택, 힙, 라이브러리 등의 중요 영역의 주소를 난수화 후 배치

 

(2) Stack Guard(스택 가드)

RET,  복귀 주소 근처에 CANARY(카나리아)라는 특수한 문자열을 넣음

해당 문자열에 변조가 발생할 경우 프로그램 강제 종료

 

(3) Stack Shield(스택 쉴드)

함수 시작 시 복귀 주소 Return Address를 Global RET(특수 스택)에 저장.

함수 종료 시 저장된 값과 RET 값을 비교가 다를 경우 오버 플로우로 가정하여 프로그램 실행을 중단시키는 기술

 

(4) 그 외

- 버퍼가 저장할 수 있는 크기보다 많은 데이터를 입력받지 않는다.

- 프로그램이 버퍼 밖의 메모리 영역을 참조하지 않는다.

- strcpy() 등 오버플로우 위험이 있는 함수 사용을 자제한다.