드림핵 - 시스템 해킹

Memory Corruption: Stack Buffer Overflow

김가윤 2023. 3. 27. 21:42

1. 서론

 

스택 버퍼 오퍼블로우 (Stack Buffer Overflow)가

발생하는 원인이 무엇인지,

 

그리고 이 취약점이

어떤 보안 문제로

이뤄질 수 있는지 살펴본다.

 

1) 스택 오버플로우와 스택 버퍼 오버플로우의 차이점

 

스택 오버플로우 (Stack Overflow)

스택 영역이 너무 많이 확장돼서

발생하는 버그를 뜻한다.

 

반면,

스택 버퍼 오버플로우 (Stack Buffer Overflow)는

스택에 위치한 버퍼에

버퍼의 크기보다 많은 데이터가 입력되어

발생하는 버그를 뜻한다.


2. 스택 버퍼 오버플로우

 

(2-1) 버퍼

 

버퍼 (Buffer)는

데이터가 목적지로 이동되기 전에

보관되는 임시 저장소의 의미로 쓰인다.

 

예를 들어,

키보드에서 데이터가 입력되는 속도보다

데이터를 처리하는 속도가 느린

프로그램이 있다고 가정하면,

 

사이에 별도의 장치가 없다면,

키보드의 입력중에

프로그램에서 수용되지 못한 데이터는

모두 유실될 것이다.

 

abcdefgh를 입력했는데

프로그램에는 abef만 전달될 수도 있다.

 

이를 해결하고자

수신 측과 송신 측 사이에

버퍼라는 임시 저장소를 두고,

이를 통해 간접적으로 데이터를 전달하게 된다.

 

송신 측은

버퍼로 데이터를 전송하고,

 

수신 측은

버퍼에서 데이터를 꺼내 사용한다.

 

스택에 있는

지역 변수는 스택 버퍼,

 

힙에 할당된 메모리 영역은

힙 버퍼라고 불린다.

 

버퍼링
송신 측의 전송 속도가 느려서
수신 측의 버퍼가 채워질 때까지 대기하는 것을 의미한다.

(2-2) 버퍼 오버플로우

 

버퍼는

제각기 크기를 가지고 있는데,

 

int로 선언한 지역 변수는

4바이트의 크기를 갖고,

 

10개의 원소를 갖는 char 배열은

10바이트트의 크기를 갖는다.

 

일반적으로 버퍼는

메모리상에 연속해서 할당되어 있으므로,

 

어떤 버퍼에서 오버플로우가 발생하면,

뒤에 있는 버퍼들의 값이

조작될 위험이 있다.

 

Figure 2. 버퍼 오버플로우로 인한 메모리 오염


(2-3) 중요 데이터 변조

 

버퍼 오버플로우가 발생하는 버퍼 뒤에

중요한 데이터가 있다면,

 

해당 데이터가 변조됨으로써

문제가 발생할 수 있다.

 

Figure 3. 스택 버퍼 오버플로우 예제

// Name: sbof_auth.c
// Compile: gcc -o sbof_auth sbof_auth.c -fno-stack-protector
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_auth(char *password) {
    int auth = 0;
    char temp[16];
    
    strncpy(temp, password, strlen(password));
    
    if(!strcmp(temp, "SECRET_PASSWORD"))
        auth = 1;
    
    return auth;
}
int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("Usage: ./sbof_auth ADMIN_PASSWORD\n");
        exit(-1);
    }
    
    if (check_auth(argv[1]))
        printf("Hello Admin!\n");
    else
        printf("Access Denied!\n");
}

 

argv[1]

16바이트가 넘는 문자열을 전달하면,

 

이들이 모두 복사되어

스택 버퍼 오버플로우가 발생한다.

 

auth는 temp버퍼의 뒤에 존재하므로,

temp버퍼에 오버플로우를 발생시키면

auth의 값을 0이 아닌 임의의 값으로

바꿀 수 있어 실제 인증 여부와 상관없이

main함수의 if(check_auth(argv[1]))

항상 참이 된다.


(2-4) 데이터 유출

 

C언어에서 정상적인 문자열은

널바이트 문자열로 종결되며,

 

표준 출력 함수들은

널바이트를 문자열의 끝으로 인식한다.

 

Figure 5. 스택 버퍼 오버플로우와 메모리 릭

// Name: sbof_leak.c
// Compile: gcc -o sbof_leak sbof_leak.c -fno-stack-protector
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(void) {
  char secret[16] = "secret message";
  char barrier[4] = {};
  char name[8] = {};
  memset(barrier, 0, 4);
  printf("Your name: ");
  read(0, name, 12);
  printf("Your name is %s.", name);
}

 

읽고자 하는 secret버퍼와의 사이에

barrier라는 4바이트의 널 배열이 존재하는데,

 

오버플로우를 이용하여

널 바이트를 모두 다른 값으로 바꾸면

secret를 읽을 수 있다.


(2-5) 실행 흐름 조작

 

함수의 반환 주소를 조작하면

프로세스의 실행 흐름을 바꿀 수 있다.

 

main함수의 반환 주소를

0x4141414141414141로 변경해서

"Success!"라는 문자열이 출려되면 성공이다.


3. 정리

 

버퍼 오버플로우는

지정된 버퍼의 크기보다

많은 데이터가 입력되어 발생한다.

 

모든 메모리의 영역에서 발생할 수 있으며,

이를 통해

데이터 변조, 데이터 유출, 실행 흐름 조작

등이 가능할 수 있다.