본문 바로가기

개념정리/System Hacking

[System Hacking] Stack Buffer Overflow

1. 버퍼

 버퍼는 데이터가 목적지로 이동되기 전에 보관되는 임시 저장소이다.

 데이터의 처리 속도가 다른 두 장치가 있을 때, 둘 사이에 오가는 데이터를 임시로 저장해 두는 것을 완충 작용이라고 한다. 데이터의 처리 속도가 다른 두 장치 사이에 별도의 장치가 없다면, 유실되는 데이터가 발생한다. 이런 문제를 해결하고자 수신 측과 송신측 사이에 버퍼라는 임시 저장소를 두고, 이를 통해 간접적으로 데이터를 전달하게 한다.

 즉, 송신 측은 버퍼로 데이터를 전송하고, 수신 측은 버퍼에서 데이터를 꺼내 사용한다. 이렇게 버퍼를 이용하면 버퍼가 가득 찰 때까지 유실되는 데이터 없이 통신할 수 있다. 스택에 있는 지역 변수는 스택 버퍼, 힙에 할당된 메모리 영역은 힙 버퍼라고 한다.

2. 버퍼 오버플로우

 버퍼 오버플로우는 문자 그대로 버퍼가 넘치는 것이다. 버퍼는 제각기 다른 크기를 가지며, int로 선언한 지역 변수는 4 바이트, 10개의 원소를 갖는 char 배열은 10바이트의 크기를 갖는다. 만약 10바이트 크기의 버퍼에 20바이트 크기의 데이터가 들어가려면 오버플로우가 발생한다. 

 일반적으로 버퍼는 메모리상에서 연속으로 할당되기 때문에 오버플로우가 발생하면 뒤에 있는 버퍼들의 값이 조작될 위험이 있다. 버퍼 오버플로우의 종류로는 1) Stack Buffer Overflow2) Heap Buffer Overflow가 있다.

1) Stack Buffer Overflow

스택 구조 상, 할당된 버퍼들이 정의된 버퍼 한계치를 넘는 경우, 복귀 주소를 변경하여 공격자가 임의 코드를 수행할 수 있다. 즉, 스택 버퍼 오버플로우는 중요 데이터 변조, 데이터 유출, 실행 흐름 조작 등 수행할 수 있다.

  • 중요 데이터 변조
    버퍼 오버플로우가 발생하는 버퍼 뒤에 중요한 데이터가 있다면 해당 데이터가 변조되면서 문제가 발생한다.

  • 데이터 유출
    C언어에서 정상적인 문자열은 널바이트로 종결되며, 표준 문자열 출력 함수들은 널바이트를 문자열의 끝으로 인식한다. 하지만 오버플로우를 발생시켜서 해당 버퍼와 다른 버퍼 사이에 있는 널바이트를 제거하면 해당 버퍼를 출력시켜서 다른 버퍼의 데이터를 읽을 수 있다.

  • 실행 흐름 조작
    함수를 호출할 때 반환 주소를 스택에 쌓고, 함수에서 반환될 때 꺼내서 원래의 실행 흐름으로 돌아간다. 이는 실제로, 함수의 반환 주소를 조작하여 프로세스의 실행 흐름을 바꿀 수 있다.

EX) 드림핵 스택 버퍼 오버플로우 예제

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int check_auth(char *password) {
    int auth = 0;
    char temp[16];
    
    //입력받은 패스워드를 temp에 복사한다.
    strncpy(temp, password, strlen(password));     
    
    //strcmp 함수를 사용하여 temp와 "SECRET_PASSWORD" 문자열 비교한다.
    //strcmp 함수는 같으면 0, 첫번째 인자가 크면 음수, 두번째 인자가 크면 양수 반환한다.
    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);
    }
    //argv[1]을 check_auth 함수의 인자로 전달한 후, 반환 값을 받아온다.
    if (check_auth(argv[1]))
        printf("Hello Admin!\n");
    else
        printf("Access Denied!\n");
}

- 코드설명
1. main 함수는 argv[1]을 check_auth 함수의 인자로 전달한 후, 반환 값을 받아온다.
2. check_auth 함수는 16바이트 크기의 temp 버퍼에 입력받은 패스워드를 복사한 후, "SECRET_PASSWORD" 문자열과 비교한다. 문자열이 같다면 auth를 1로 설정하고 반환한다.
3. 반환 값이 0이면 "Access Denied", 1이면 "Hello Admin"을 출력한다.

- 스택 버퍼 오버플로우
1. check_auth에서 strncpy 함수를 통해 temp 버퍼를 복사할 때, temp의 크기인 16바이트가 아닌 password의 크기만큼 복사한다. => argv[1]에 16바이트가 넘는 문자열을 전달하면, 스택 버퍼 오버플로우가 발생할 것이다.
2. auth는 temp 버퍼의 뒤에 존재하므로, temp 버퍼에 오버플로우를 발생시키면 auth의 값을 0이 아닌 임의의 값으로 바꿀수 있다. 그러면 main 함수의 if(check_auth(argv[1]))는 항상 참이 된다.

2) Heap Buffer Overflow

힙 구조 상, 최초 정의된 힙의 메모리 사이즈를 초과하는 문자열들이 힙의 버퍼에 할당될 시, 공격자가 데이터 변경 및 함수 주소 변경으로 임의 코드를 수행할 수 있다.

 

'개념정리 > System Hacking' 카테고리의 다른 글

[System Hacking] 쉘 코드 모음(32비트, 64비트)  (0) 2022.08.14
[System Hacking] 함수 호출 규약  (0) 2022.08.09
[System Hacking] Shellcode  (0) 2022.08.02
6. pwntools  (0) 2022.08.01
5. gdb 디버거  (0) 2022.07.21