간단 지식/System Programming

10. buffer overflow

납작한돌맹이 2021. 1. 8. 00:41
반응형

버퍼 오버플로우란 프로세스가 데이터를 버퍼에 저장할 때 개발자가 지정한 곳 밖에 저장하여, 범위 밖의 데이터가 인접 메모리를 overwrite함으로써 오류가 발생하는 현상을 말한다. 여기서 버퍼란, 일시적으로 데이터를 보관하는 메모리의 영역이다.

 

대표적으로 버퍼 오버플로우를 일으키는 원인을 제공할 수 있는 함수가 하나 있다. 바로 gets()이다. 아래는 gets()의 원형코드이다.

char *gets(char *dest){
    int c = getchar();
    char *p = dest;			----------(1)
    while(c != EOF && c != '\n'){
    	*p++ = c;
        c = getchar();
    }
    *p = '0';
    return dest;
}

코드라인 (1)에 따라 gets()는 버퍼의 시작주소를 포인터변수 p에 할당한다. 그러나 시작주소만으로는 버퍼가 어디까지 안전하게 할당되는지 알 수 없다. 즉, 문자를 읽는데 제한할 방법이 없다는 것이다. 이와 같이 버퍼의 경계를 탐색하지 않는 c표준 라이브러리 함수에는 strcpy, strcat, sprintf 등이 있다. 따라서 이왕 사용할 것이면 fgets, strncpy 처럼 안전한 함수를 사용하는 것이 좋다. 같은 의미로 문자열을 read할 때 scanf 보단 fgets를 사용하는게 더 안전하다.

 

버퍼에 코드가 어떻게 저장되는지 자세하게 알아보자. 

void test(){
    char buf[4];
    gets(buf);
    puts(buf);
}

위 코드에서 gets(buf)를 호출하기 전에는 스택은 이런 형태를 가질 것이다.

gets()를 호출하면 당연히 test()로 돌아올 수 있도록 return address가 있어야한다. 만일 return address가 4006f6이라면 아래와 같이 8byte가 채워진 스택이 될 것이다. (참고로 스택은 little endian 방식으로 채워진다.)

우리가 할당한 버퍼는 4byte이다. 4byte를 넘는 문자열을 읽어들이면 overflow와 동시에 segmentation fault가 발생한다. (segmentation fault는 허용되지 않은 메모리 영역에 접근을 시도하거나, 허용되지 않은 방법으로 메모리에 접근할 시 발생하는 오류) 다만 이 상황에서는 스택에 20byte만큼의 여유공간이 있기 때문에 읽어들이는 문자열이 총 24byte이하라면 4byte를 넘기더라도 버퍼 오버플로우더라도 프로그램이 종료되는 일은 없다.

 

만일 스택의 여유공간을 넘어선 문자열을 읽어들이면 스택 오버플로우, segmentation fault와 함께 프로그램이 강제 종료될 것이다. 그러나 gcc 4.1 이후로는 스택 오버플로우를 방지하기 위해 SSP(Stack Smashing Protector)기능이 내장되어있다. 따라서 아래 이미지와 같은 결과를 볼 수 있다.

 

 

 

(이 글이 도움이 됐다면 광고 한번씩만 클릭 해주시면 감사드립니다, 더 좋은 정보글 작성하도록 노력하겠습니다 :) )

반응형