시스템해킹] 스택 버퍼오버플로우(Stack BufferOverFlow) 란 ??? ![]() ![]() 2014/01/12 19:57
|
Stack Buffer Over Flow
- Written by Park Byung Wook
- 2012년 6월 작성
- 버퍼오버플로우(Buffer Over Flow) 란?
버퍼오버플로우(Buffer Over Flow)란 메모리를 다루는 곳에 오류가 발생하여 잘못된 동작을 하는 프로그램 취약점이다. 프로세스가 데이터를 버퍼에 저장할 때 지정된 크기 이상의 크기의 데이터 양이 입력 되었을 때 나타나는 오류이다. 지정된 범위를 벖어난 데이터는 인접한 메모리를 덮어 쓰게 되는데 다른 데이터가 포함 되어 있을 가능성이 있다. 이로 인해 프로그램의 실행이 원래 의도 하던 형태가 아닌 다른 형태로 실행 될 수 있으며, 메모리 접근오류, 잘못된 결과, 프로그램 종료, 또는 시스템 보앆 누설 등이 발생 한다. 이것은 크래커에 의해 악의적으로 사용될 가능성이 있기 때문에 이를 방어할 방지책이 필요하다.그리고 버퍼오버플로우(Buffer Over Flow)는 데이터를 저장하는 과정에서 그 데이터가 저장되어야 할 메모리의 위치가 정당한지 아닌지를 검사하지 않아서 발생한다. 그러므로 메모리 저장 시 저장될 메모리의 시작과 끝을 검사하는 등의 방법으로 방지 할 수 있다.
공격 방법
1. NOP 을 이용한 공격
우리는 이제부터 버퍼오버플로우에 취약한 bof라는 프로그램 하나를 볼 것이다.
아래와 같이 bof라는 프로그램은 프로그램 실행 시 입력 받은 파라메타 값을 출력 해준다.

이 프로그램을 자세히 알아보기 위해 gdb 라는 디버거 를 이용해서 열어 보겠다.
Gdb 로 프로그램을 디버깅 해보기 전에 일단 bof파일을 bof1으로 복사를 한다.
왜냐하면 setuid가 걸린 프로그램은 gdb로 실행하면 권한 관련 오류가 나타나기 때문이다. 그래서 유저로 해당 프로그램을 복사를 해서 그 복사한 프로그램을 디버깅 한다.
Disassemble main 명령어로 어셈블러로 된 프로그램 코드를 가져 온다.
AT&T방식으로 되어있어서 코드가 보기가 힘들다. 그러므로
Set disassembly-flavor intel 명령어로 인텔 형태로 바꿔준다.

좀 깨끗하게 보인다.
이제 이 어셈 코드를 분석해 보겠다.
먼저 <main+0>을 보면 ebp를 push해준다.
그리고 ebp에 esp 값을 복사 해준다.
그럼 ebp는 esp가 가리키는 곳을 가리키게 된다.
그 다음 <main+3>을 보면 esp를 0x218만큼 빼준다. 그러므로 현재 스택의 틀은 아래와 같이 된다.

그리고 다음 3줄 은 계산해 보면 알겠지만 스택에 어떠한 영향을 주진 않는다.
이러한 계산을 하는 이유는 컴퓨터 내부적으로 틀을 맞추기 위한 것이다.
다음 <main+19>부터 보면 esp를 0x8만큼 더 빼고 있다. 그리고 그 다음 줄에 eax에 [ebp+12] 의 값을 복사하고 있다.
여기서 [ebp+12]의 위치는 ret 위 쪽인데 이곳에는 argv[]변수 들이 위치 한다.
<main+22>에서 Argv[0]의 주소 값을 eax로 복사하고 다음 줄에서 0x4 만큼 더해서 eax가 argv[1]을 가리키게 한다.
그리고 현재 eax가 가리키는 값을 push한다.
여기서 스택의 현 상태를 생각 해보면 아래와 같다.
그리고 다음에서 버퍼오버플로우 에서 가장 중요한 취약 함수 strcpy 가 나타난다.
함수가 콜이 될 때는 콜 하기 전 인자 값을 먼저 push한다.
여기서 push해주는 인자 값의 위치는 바로 [ebp-536]이다.
그렇다면 strcpy함수 안에서 전달 받은 argv[1]의 문자열을 [ebp-536]의 위치로 복사한다는 것을 의미한다.
코드 아래 printf함수가 있는 부분을 보면 인자 값으로 [ebp-536] 부분을 push하는 모습을 볼 수 있다.
그러므로 bof프로그램 을 실행 시킬 때 전달되는 문자열은 [ebp-536] 위치부터 복사가 되어진 다음printf로 출력 되는 것이다.
그렇다면 버퍼오버플로우 공격을 할 때 [ebp-536]의 위치부터 ebp부분까지 를 채워 줘야 하니 540 byte만큼을 아무 값으로 채워 주고 쉘코드를 삽입하면 될 것이다.
[ NOP + 쉘코드 =(ebp부터 [ebp-536]부분) + 예상되는 쉘코드 주소(ret부분)]
일단 esp의 주소 값을 출력하는 프로그램 을 하나 만들자.
이와 같은 프로그램을 만든 이유는 프로그램을 실행 시킬 때 마다 스택의 주소 값은 바뀌기 때문이다.
이 프로그램을 몇 번 실행 시켜보면 아래와 같이 계속적으로 주소 값이 바뀌는 것을 볼 수 있다.
그런데 주소 값들을 잘 보면 주소의 변화가 크게 없다는 것을 알 수 있다. 그러므로 우리는 이 주소들 중 하나를 집어서 그 값이 만약 우리가 공격할 프로그램에 넣어줄 쉘코드의 주소 값과 같아지면 쉘코드가 실행될 것이다.
원리를 알았으니 공격을 해보자.
공격은 스크립트 언어인 perl언어를 사용 할 것이다.
일단 빈 공간을 얼마나 채워야 할지를 계산 해야 한다.
우리가 사용할 쉘코드는 아래 와 같은 것인데, 이것의 크기가 61byte이다.
\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68
그러므로 540byte에서 61byte를 뺀 479byte를 아무 일도 하지 않는 NOP(1byte) 이라는 것으로 채우겠다.
그럼 공격을 하기 위해 주소 값을 하나 정하고 공격을 해보겠다.

위를 보면 \x라는 것이 붙어 있는데, 이는 16진수를 의미한다.
“\x90”x479라고 되어있는 부분이 NOP을 479byte만큼 채워 주는 명령이고 그 뒤가 쉘코드, 그 다음으로 스택 주소가 나오는데, 이 주소는 1byte씩 거꾸로 적어 줘야한다. 그 이유는 우리가 사용하는 cpu의 메모리 적재 방식이 리틀 엔디언 을 따르기 때문이다.
엔터를 치기 전에 언어 방식을 한국으로 바꿔줘야 한다.
엔터를 쳐보면 운이 정말 좋지 않고서는 한번에 되지 않을 것이다. 왜냐하면 위에서 말했듯이 스택의 주소는 프로그램 실행 때마다 변하기 때문이다. 그러므로 계속 쳐줘야한다.
여러 번의 시도 끝에 루트 권한을 획득 하였다.
2. 환경 변수를 통한 공격
NOP을 이용한 공격에서 보았듯이 한번에 공격이 들어가는 것이 아니라 여러 번 시도를 해줘야 한다.
그러므로 한번에 성공 하는 공격방법이 있는데 그것이 바로 환경변수를 이용하는 공격이다.
이번 공격에서도 위에서 사용했던 취약 프로그램을 이용할 것이다.
일단 환경 변수의 주소를 구하는 프로그램을 하나 만들자.
아래 소스의 프로그램이 입력된 환경변수의 주소 값을 알려준다.
이제 환경 변수를 만들어 볼 것이다.
환경 변수 이름을 BOF_A로 하겠다.
환경 변수 안의 입력된 내용을 보면 뒤쪽에는 쉘코드가 있는데 앞쪽에 NOP을 100개 넣어 놓았다.
그 이유는 환경 변수는 프로그램 이름의 길이에 영향을 받기 때문이다.
환경 변수의 스택 에서의 위치는 실행 프로그램의 argv[] 위 쪽에 위치 하고 있다. 그런데 우리는 프로그램을 실행 시킬 때 argv[0]에는 실행시키는 프로그램의 이름이 들어간다는 것을 알고 있다. 그렇다면 프로그램 이름의 길이가 생각 보다 길어서 환경 변수가 있는 곳 까지 올라 가는 경우가 생길 수 있다.
다시 한번 더 설명을 하자면,
환경 변수 주소를 구하는 프로그램의 이름의 길이는 env로 3글자 이다. 그런데 만약 공격할 프로그램의 이름의 길이가 10글자 정도 가 된다면 env프로그램으로 구한 환경 변수의 주소 값과 공격 할 프로그램의 환경 변수의 주소 값이 다르기 때문에 공격이 안 된다. 공격 할 프로그램의 이름이 길이가 3글자 보다 짧거나 같으면 환경 변수의 주소 값에 영향을 주지 않으므로 상관이 없지만 더 길다면 문제가 생긴다. 그렇기 때문에 앞 쪽에 NOP을 두어서 프로그램 이름 길이 차이에 대한 주소 값 차이를 메워주어야 한다.
만약 환경 변수의 주소를 알아보기 위한 프로그램의 이름이 env와 env1이 있다면 다음과 같이 같은 환경 변수를 입력 하면 1글자 길이 차이 때문에 주소 값이 2byte라는 차이가 생긴다.
( 절대 경로를 통해 env프로그램을 실행 시켜 구한 환경 변수 주소로 상대 경로를 이용해 취약 프로그램을 공격하면 실패함.)
이제 이 BOF_A라는 환경 변수가 위치한 주소를 알아보자.
env프로그램을 이용하면 아래와 같이 나온다.
위에서 ebp까지의 540byte였으므로 NOP을 540개 주고 환경 변수의 주소를 넣어 준다.
루트권한을 얻은 것을 확인 할 수 있다.
스택버퍼오버플로우 는 데이터를 저장하는 과정에서 그 데이터가 저장되어야 할 메모리의 위치가 정당한지 아닌지를 검사하지 않아서 발생한다. 그러므로 메모리 저장 시 저장될 메모리의 시작과 끝을 검사하는 등의 방법으로 보안 하며 Strcpy, strcat, getwd, gets, fscanf, scanf, realpath, sprint 등 과 같은 취약 함수 사용을 자제 해야 한다.
'해킹,보안' 카테고리의 다른 글
게임 보안 체크 사항들 (0) | 2014.02.11 |
---|