Buffer Overflow 개념정리 1 - Buffer Overflow의 역사와 발생 원리 및 종류

Buffer Overflow 개념정리 1 - Buffer Overflow의 역사와 발생 원리 및 종류

in

Buffer Overflow는 Buffer OverRun 이라고도 하며 소프트웨어 응용 프로그램 내의 메모리 영역이 주소 경계를 넘어 인접한 메모리 영역을 침범해 잘못된 동작을 야기하는 소프트웨어 취약점 입니다.

이 글은 Buffer Overflow 의 역사, 발생 원리와 종류에 대해서 설명합니다.

Buffer Overflow의 역사

Buffer Overflow의 역사는 1970년대로 거슬러 올라가지만, 사실상 1980년 후반이 되어서야 Buffer Overflow를 악용한 사례가 등장합니다. 그것은 바로 1988년 11월 Robert Morris가 만든 “Morris Worm” 입니다.

“Morris Worm” 은 인터넷을 통해 배포된 가장 오래된 Worm 중의 하나입니다.

당사자의 말에 따르면 당시 Network의 크기를 파악하고자 만든 자가복제 프로그램으로, 목적상 다른 컴퓨터로 전이, 전파되는 기능이 가장 중요 한데, 이를 위해 Unix의 fingerd(핑거 데몬) 서비스의 Buffer Overflow 결함을 이용 하여 Worm을 확산 시킵니다.

fingerd?

네트워크 상의 특정인 혹은 특정 컴퓨터 시스템의 상태를 제공하기 위한 프로토콜이며, Well-known port 79번을 통해 fingerd에 접속을 요청합니다.

서버측에서는 RUIP (Remote User Information Program)이 실행되어 클라이언트측으로부터의 질의(query)를 처리하여 답변을 생성, 전달하고, 이를 클라이언트에서 사용자에게 적합한 형태로 가공하여 보여줍니다.

인터넷 초창기에는 자주 사용되던 명령어였으나, 사용자의 이메일 주소, 이름 등의 정보가 해커들에게 악용되기 시작하고, fingerd의 헛점을 이용한 해킹 방법이 알려지면서, 1990년대 후반경에는 대다수의 호스트들이 fingerd를 제공하지 않게 되었습니다.
References: https://ko.wikipedia.org/wiki/핑거_(프로토콜)

이후 Buffer Overflow는 “Aleph One”이라는 사람이 1997년 Phrack(프랙)이라는 온라인 보안 잡지에 “Stack Buffer Overflow”에 관한 문서를 게재하면서 더욱 널리 알려지게 됩니다.

Phrack - Aleph One

https://github.com/rootkiter/phrack/blob/master/phrack49/14.txt

Buffer Overflow 발생 원리

Buffer Overflow의 원리를 이해하기 위해서는 프로그램이 실행될 때 메모리상에 데이터와 함수가 어떻게 저장되는지 알아야 합니다.
아래 그림은 Stack 영역에서 발생하는 Overflow를 보여주고 있습니다.

image Stack Buffer Overflow 발생 원리

만약, 그림에 대한 이해가 쉽지 않다면 아래 링크를 참고합니다. https://security.stackexchange.com/questions/135786/if-the-stack-grows-downwards-how-can-a-buffer-overflow-overwrite-content-above

하나의 프로그램에는 여러 함수들과 변수가 포함되어 있습니다. 각 함수가 호출될때 Stack 영역에 해당 함수의 지역 변수들과 RET(Return Address)가 저장 됩니다.
RET 에는 호출한 함수가 종료될 때 OS가 프로그램의 제어권을 함수를 호출한 프로그램에게 다시 넘겨주기 위한 복귀 주소가 담깁니다.

일반적인 Stack Buffer Overflow Attack은 RET(복귀 주소)가 지역 변수의 데이터에 의해 침범 당해 발생됩니다.

만약, 사용자가 실수로 해당 지역 변수의 데이터를 기존에 할당된 크기보다 많은 데이터를 입력을 하게 된다면(위그림 b) RET에 저장된 복귀 주소값이 다른 값으로 변조 될 수 있습니다.
이후 해당 함수가 종료되어 IP(Instruction Pointer)가 RET 주소로 복귀할 때, 엉뚱한 주소값으로 복귀하게 되고 이는 프로그램의 비정상적인 동작으로 이어지게 됩니다.

해당 결함을 악용 하면, 공격자는 함수 호출시 복귀하는 RET 값을 원하는 값으로 덮어씌워서 악의적인 코드가 실행 되도록 할 수 있습니다.

위 설명은 Stack 영역에서 이루어지는 “Stack Buffer Overflow” 이며, Stack 영역말고도 Heap 영역 수행되는 “Heap Overflow”도 존재합니다.
또한 메모리를 직접 덮어씌워 특정 코드를 실행하게 하지는 않지만 프로그램을 비정상적인 종료로 이어지도록 유도하는 “Integer Overflow”도 존재합니다.

Overflow는 주로 데이터를 저장하는 과정에서 그 데이터를 저장할 메모리 위치가 유효한지를 검사하지 않아서 발생합니다.

흔히 Buffer Overflow에 취약한 프로그래밍 언어는 C와 C++ 입니다. 그 이유는 할당된 메모리 공간 내에서 데이터 접근 또는 덮어쓰기에 대한 보호 기능을 기본적으로 제공하지 않으며, 어떤 배열에 기록되는 데이터가 그 배열의 범위 안에 포함되는지 자동으로 검사하지 않기 때문입니다.

반면 C#은 OverFlow Detection 기능을 제공하며 Java, Perl, Python과 같은 인터프리터 언어도 메모리를 자체적으로 관리하는 기능을 제공합니다.

Buffer Overflow의 종류

Buffer Overflow는 다양한 종류가 존재하지만, 크게 Stack Buffer Overflow, Heap Buffer Overflow 로 나뉩니다.

또한 Buffer Overlfow와 달리 다른 영역을 침범하는 일반 Overflow도 존재합니다.

Overflow?

Buffer Overflow는 해당 Buffer 내부에서 이루어지기 때문에 Buffer Overflow 라고 불립니다.
그러나 Buffer를 넘어서 다른 세그먼트를 침범하는 일반 Overflow도 존재합니다.

Overflow도 다양한 종류가 존재하지만, Buffer Overflow와 마찬가지로 “Heap Overflow” 그리고 “Stack Overflow” 로 나뉩니다.
소프트웨어 Exploit 코드에서 주 Overflow 대상이 되는 영역은 Stack과 Heap 영역입니다.

Heap은 메모리상 낮은 주소에서 높은 주소로 할당되며 Stack은 높은 주소에서 낮은 주소로 할당되어 각 영역이 상대 공간을 침범할 수 있습니다.
이때 Heap이 Stack을 침범하는 경우를 Heap Overflow 라고 하고, Stack이 Heap을 침범하는 경우를 Stack Overflow 라고 합니다.
image Overflow

1) Stack Buffer Overflow (Stack-based Exploitation)

Stack 영역에서 발생하는 Buffer Overflow 입니다.

공격자는 Stack 영역의 Overflow를 악용하여 프로그램 동작을 조작할 수 있으며 아래와 같이 다양한 방법으로 악용될 수 있습니다.

  • 프로그램의 동작에 영향을 끼치는 취약한 버퍼에 위치한 로컬 변수를 공격자에게 유리한 데이터로 덮어 씌운다.
  • Stack Frame의 RET 주소를 shell-code라고 하는 악의적인 코드를 가리키도록 덮어 씌운다. 호출된 함수가 종료되면 프로그램은 shell-code를 실행하게 된다.
  • 이후에 실행되는 함수 포인터 또는 예외 처리기를 shell-code를 가르키게 하도록 덮어씌운다.
  • 호출된 함수에서 해당 함수를 호출한 함수의 로컬 변수 또는 포인터를 덮어씌운다.

즉, 공격자는 악의적인 데이터를 설계한 다음, 해당 데이터를 버퍼에 배치 하여 RCE 취약점을 발생 시킵니다. 그렇기 때문에 Stack Corruption(스택 오염) 이라고도 불립니다.

만약 Buffer Overflow를 하는데 필요한 Memory Address를 쉽게 예측할 수 없다면 공격 난이도는 그만큼 올라갑니다.
이때 Trampolining 이란 기술을 사용하면 위와 같은 상황에서도 Buffer Overlfow Attack을 수행할 수 있다고 합니다.

Trampolining

사용자가 지정한 데이터의 주소값을 모를때 레지스터에 그 주소가 저장되어 있으면 RCE를 유도하도록 RET의 복귀 주소를 덮어씌우는 기술입니다.

또한 Fuzzing을 이용해 Buffer Overflow 포인트를 찾을 수도 있습니다.

Finding Buffer Overflow with Fuzzing

아래 URL을 참고합니다.
https://liveoverflow.com/finding-buffer-overflow-with-fuzzing/

2) Heap Buffer Overflow (Heap-based Exploitation)

Heap 영역에서 발생하는 Buffer Overflow 입니다.
Heap에서 발생하는 Buffer Overflow는 Stack 영역에서 발생하는 Buffer Overflow 와는 다른 방식으로 악용 될 수 있습니다.

Heap의 메모리는 런타임시 Application에 의해 동적으로 할당되며, 일반적으로 동적으로 할당된 프로그램 데이터가 저장됩니다. 이러한 특성을 이용해 해당 데이터를 특정 방법으로 오염시켜 Application이 linked list pointer(연결 리스트 포인터) 등과 같은 내부 자료 구조를 덮어쓰는 방식으로 악용합니다.

동적 메모리 할당 연결(malloc meta data)를 덮어씀으로써 프로그램의 함수 포인터를 조작하는 방식이 일반적입니다.