Buffer: 두 판 사이의 차이
| 72번째 줄: | 72번째 줄: | ||
위 예시가 말하고 있는 것은 해커가 입력값을 어떻게 주느냐에 따라 return address의 값이 그대로 바뀌므로, 악의적인 해커에 의해서 control flow가 바뀔 수 있다는 것이다. 이와 같이 해커가 control flow를 탈취하는 것을 control flow라고 한다. 이는 매우 치명적이고도 고전적인 문제이므로, 프로그램을 설계할 때 이와 같은 문제를 야기할 수 있는 함수들의 사용은 가급적이면 피해야 한다. | 위 예시가 말하고 있는 것은 해커가 입력값을 어떻게 주느냐에 따라 return address의 값이 그대로 바뀌므로, 악의적인 해커에 의해서 control flow가 바뀔 수 있다는 것이다. 이와 같이 해커가 control flow를 탈취하는 것을 control flow라고 한다. 이는 매우 치명적이고도 고전적인 문제이므로, 프로그램을 설계할 때 이와 같은 문제를 야기할 수 있는 함수들의 사용은 가급적이면 피해야 한다. | ||
==Real-world Example: Morris Worm== | ===Real-world Example: Morris Worm=== | ||
BOF는 1988년에 배포된 최초의 인터넷 멀웨어(malware)인 모리스 웜(morris worm)을 구현하는 데 사용된 보안 취약점이라고 알려져있다. 해당 멀웨어는 사용자 정보<ref>로그인 여부, 메일 등이 있다.</ref>를 조회하는 finger 유틸리티를 대상으로 하였다. 이때 finger 유틸리티는 정상적으로는 아래와 같이 입력한다: | |||
<syntaxhighlight lang="bash"> | |||
$ finger jschoi@cspro.sogang.ac.kr | |||
</syntaxhighlight> | |||
이때 문제는 구형의 finger 서버가 네트워크 입력을 안전하게 처리하지 못했다는 것이고, 이 때문에 BOF가 발생할 수 있었다. 모리스 웜은 이를 악용하여 아래와 같이 finger 요청에 매우긴 문자열과, 조작된 return address를 포함시킨 입력을 포함시켰다: | |||
<syntaxhighlight lang="bash"> | |||
$ finger "<long string>+<new return address>" | |||
</syntaxhighlight> | |||
이렇게 하면 서버에 BOF가 발생해 return address가 해커가 지정한 위치로 덮어쓰기 되고, 결과적으로 서버에서 웜이 실행되어 control hijack가 발생한다. | |||
==각주== | ==각주== | ||
[[분류:컴퓨터 시스템]] | [[분류:컴퓨터 시스템]] | ||
2025년 5월 20일 (화) 01:05 판
상위 문서: 컴퓨터 시스템
개요
버퍼(Buffer)란 데이터를 임시로 저장하는 배열이다. 예를 들면, 동영상 스트리밍 시, 끊김없는 재생을 위해서 일부 데이터를 저장하는 경우가 있으며, 이를 비디오 버퍼링(vedio buffering)이라고 한다. C언어에서는 보통 char buf[256];과 같이 선언되는 문자 배열을 의미하며, 사용자의 입력 문자열을 저장하는데 자주 사용된다.
Classic Buffer Overflow
스택 기반 오버플로우(Stack-based buffer overflow)란 스택 메모리에 존재하는 버퍼에 데이터가 초과되어 쓰이면서, 다른 메모리 영역까지 덮어쓰는 오류이다.[1] 해당 오류의 주된 원인은 gets(), scanf("%s"), strcpy(), strcat()와 같은 함수들을 배열 크기를 고려하기 않고 사용하는 것이다. 해당 오류는 해커가 이를 이용해 시스템을 공격하거나 악성코드를 주입할 수 있어서 보안상 매우 위험하다.
Example Program with BOF
아래는 스택 기반 오버플로우 오류가 발생할 수 있는 C언어 코드의 예시이다:
void echo(void) {
char buf[8];
gets(buf); //Never use this function. 너무 많은 BOF의 원인
puts(buf);
}
int main(void) {
echo();
return 0;
}
jschoi@ubuntu:~$ ./bof
Hello
Hello
jschoi@ubuntu:~$ ./bof
0123456789ABCDE
0123456789ABCDE
jschoi@ubuntu:~$ ./bof
0123456789ABCDEF
0123456789ABCDEF
Segmentation fault
위 코드에서 gets(buf)는 입력 크기를 검사하지 않으므로, 8바이트 초과 입력 시 버퍼를 넘쳐서 다른 데이터를 덮어 쓴다. 이때, 동일하게 8바이트 버퍼를 초과한 입력 "0123456789ABCDE"은 괜찮고, "0123456789ABCDEF"에서 비로소 오류가 생기는 이유는 아래 어셈블리 코드를 관찰하여 알 수 있다:

(gdb) disassemble echo
0x401136: sub $0x18,%rsp
0x40113a: lea 0x8(%rsp),%rdi
0x40113f: mov $0x0,%eax
0x401144: call 0x401040 <gets@plt>
0x401149: lea 0x8(%rsp),%rdi
0x40114e: call 0x401030 <puts@plt>
0x401153: add $0x18,%rsp
0x401157: ret
(gdb) disassemble main
0x401158: sub $0x8,%rsp
0x40115c: call 0x401136 <echo>
0x401161: mov $0x0,%eax
0x401166: add $0x8,%rsp
0x40116a: ret
이때 해당 코드는 figure 1과 같이 스택 프레임을 형성한다. 어셈블리 코드에서 call 0x401040 <gets@plt> 명령어가 호출되면 입력된 문자열의 각 문자들은 lower address 부터 순차적으로 char buf[8]에 저장된다. 먼저, "Hello"가 입력된다면 아래와 같이 메모리에 저장된다:

이는 주어진 char buf[8]에 할당된 메모리 공간을 벗어나서 입력 문자들을 정상적인 입력이다. 5글자를 입력하였으나 실제로는 6개의 바이트가 저장된 이유는 문자열은 끝에 NULL 문자로 종료되어야 하기 때문이다. 이때 "0123456789ABCDE"를 입력한다면, 아래와 같이 메모리에 저장된다:

이는 char buf[8]에 할당된 메모리 공간을 분명히 침해하였다. 하지만 그럼에도 문제가 생기지 않는 이유는, 침해된 공간이 어셈블리 코드 상에서 사용되지 않는 메모리 공간이었기 때문이다. 따라서 char buf[8]에 할당된 메모리 공간을 벗어나 침범한 문자들은 어떤 중요한 데이터를 덮어쓰지 않았고, 이에 따라 프로그램은 정상적으로 수행된다. 하지만, "0123456789ABCDE"를 입력한다면, 아래와 같이 메모리에 저장된다:

비로소 메모리 상에서 문제가 생긴 것을 관찰할 수 있다. 이는 입력된 문자열 "0123456789ABCDEF"가 retrun address가 저장된 주소 공간을 침범하였기 때문이다. 이는 결과적으로 프로그램이 echo() 함수 호출이 끝난 후 "0x401100"에 해당하는 주소 공간으로 돌아가도록 한다. 하지만 이는 적절한 return address가 아니므로, 프로그램에게 결과적으로 오작동과 크래시(crash)를 야기한다. 이는 단순히 프로그래머나 유저의 실수에서 그치는 일이 아니다. 만약 악의적인 해커가 함수 호출이 끝난 후 "0x636261"으로 프로그램이 점프하기를 원한다면, 아래와 같이 입력을 줄 수 있고, 이에 따라 메모리 공간은 figure 5와 같이 바뀐다.
jschoi@ubuntu:~$ ./bof
0123456789ABCDEFabc
0123456789ABCDEFabc
Segmentation fault

위 예시가 말하고 있는 것은 해커가 입력값을 어떻게 주느냐에 따라 return address의 값이 그대로 바뀌므로, 악의적인 해커에 의해서 control flow가 바뀔 수 있다는 것이다. 이와 같이 해커가 control flow를 탈취하는 것을 control flow라고 한다. 이는 매우 치명적이고도 고전적인 문제이므로, 프로그램을 설계할 때 이와 같은 문제를 야기할 수 있는 함수들의 사용은 가급적이면 피해야 한다.
Real-world Example: Morris Worm
BOF는 1988년에 배포된 최초의 인터넷 멀웨어(malware)인 모리스 웜(morris worm)을 구현하는 데 사용된 보안 취약점이라고 알려져있다. 해당 멀웨어는 사용자 정보[2]를 조회하는 finger 유틸리티를 대상으로 하였다. 이때 finger 유틸리티는 정상적으로는 아래와 같이 입력한다:
$ finger jschoi@cspro.sogang.ac.kr
이때 문제는 구형의 finger 서버가 네트워크 입력을 안전하게 처리하지 못했다는 것이고, 이 때문에 BOF가 발생할 수 있었다. 모리스 웜은 이를 악용하여 아래와 같이 finger 요청에 매우긴 문자열과, 조작된 return address를 포함시킨 입력을 포함시켰다:
$ finger "<long string>+<new return address>"
이렇게 하면 서버에 BOF가 발생해 return address가 해커가 지정한 위치로 덮어쓰기 되고, 결과적으로 서버에서 웜이 실행되어 control hijack가 발생한다.