> Buffer Overflow
버퍼란 사용자가 데이터를 저장하기 위한 공간을 의미한다. 지금은 스택에서 생성되는 공간에 대하여 다루겠다. 스택은 저번 게시물과 같이 어떻게 구성되어 있는지에 대하여 공부했다. 버퍼오버플로우를 이해하는데 있어서 가장 핵심적으로 봐야할 부분은 base pointer와 return address이다. 스택의 바닥부분을 가리키고 있는 것이 base pointer이다. 32bit binary 에서는 ebp라고 부르며 레지스터의 일종이다. 그리고 base pointer가 가리키는 부분을 Stack Frame이라고 부르며 따라서 base pointer를 Stack Frame Pointer(SFP)로 부르기도 한다. Return address는 프로그램이 종료되면서 ret 명령에 따라서 eip에 들어가게 될 명령어의 주소를 담고있는 부분이다. 줄여서 RET라고도 부르기도 한다. Base pointer 와 return address가 왜 스택의 바닥에 위치해 있고 왜 이런 역할을 하는 지에 대한부분은 앞에서 다루었기 때문에 지금은 buffer overflow취약점을 통해 exploit을 하는 것에 중점을 두겠다.
Buffer overflow는 메모리에 할당된 공간인 buffer를 넘어서 SFP와 RET영역까지 사용자가 원하는 값을 채워넣을 수 있는 취약점을 말한다. 기초적인 BOF를 하는 입장이라면 오버플로우가 터지는 현상을 가장 많이 볼 수 있는 것은 strcpy()함수이다. 이러한 함수들로 인해서 buffer가 overflow되고 SFP, RET를 수정할 수 있게 된다면, 다음에 실행할 명령어의 주소를 담고있는 eip에 담길 값을 조작하는 것이기 때문에, 프로그램이 해야 할 일을 조종할 수 있다. 프로그램 실행 중에 어떠한 공간에 shell code를 집어넣고 집어넣은 shell code의 주소를 RET영역에 넣게 됨을로써 shell을 따는 것이 가장 고전적인 BOF의 방법이다.
#include<stdio.h> int main(){ char buffer[40]; read(0, buffer, 0x400); return 0; }
이러한 바이너리가 있다고 하자. 그렇다면 프로그램 실행 후, read()함수가 작동하
기 전까지의 메모리의 상황을 가시적으로 그려보면 이러한 방식으로 되어있을 것이다.
Buffer |
SFP |
RET |
이 상태에서 RET 뒤의 영역에 shellcode를 집어넣고 RET영역에는 집어넣은 쉘코드
주소를 넣으면 shell을 딸 수 있을 것이다.
dummy |
dummy |
쉘코드의 주소 |
shellcode |
<부록>
우리는 bof와 같은 취약점들을 이용해서 희생 서버의 쉘을 취득하는게 목표이다.
쉘을 취득하다는 말은 희생 서버의 관리자 권한 (command line을 조작할 수 있는 권리)을 얻는다는 것이며, 앞으로 경험하게 될 다양한 대회에서도 해당 방법으로 문제를 풀어야 된다.
쉘을 취득하는 방법중 제일 기초적인 방법이 쉘코드를 이용한 exploit이다.
쉘코드란 16진수로 이루어진 `코드`이다. 16진수의 아스키 코드로 이루어져있으며,
쉘코드란 명칭답게 이 쉘코드를 이용하면 쉘을 취득할 수 있다.