And Brain said,

버퍼 오버플로우(Buffer Overflow) 공격, 원리와 그 방어 본문

IT/보안

버퍼 오버플로우(Buffer Overflow) 공격, 원리와 그 방어

The Man 2025. 2. 11. 17:16
반응형

1. 버퍼 오버플로우(Buffer Overflow)란?

1.1 개념

버퍼 오버플로우(Buffer Overflow)는 프로그램이 예상보다 많은 데이터를 입력받아, 메모리에 저장할 수 있는 범위를 초과하는 현상을 말하는데, 이는 스택(Stack)이나 힙(Heap) 영역의 데이터가 손상되거나, 프로그램의 흐름을 제어하는 중요한 정보가 덮어써지는 결과를 초래합니다.

이 취약점은 해커가 의도적으로 악성 데이터를 입력하여 프로그램이 예측하지 못한 동작을 하도록 유도하는 데 활용될 수 있으며, 심각한 경우 공격자는 원격 코드 실행(Remote Code Execution, RCE)을 통해 시스템을 장악할 수도 있습니다.

1.2 버퍼 오버플로우가 발생하는 이유

버퍼 오버플로우는 C/C++과 같은 저수준 언어에서 발생하기 쉽다. 그 이유는 아래와 같습니다.

  1. 메모리 경계 검사를 자동으로 수행하지 않음
    → C/C++에서는 문자열을 복사할 때 크기를 확인하지 않으면 초과 입력이 발생함.
  2. 스택 구조의 한계
    → 프로그램이 실행될 때 함수 호출이 스택을 사용하며, 크기를 초과하면 이전 주소를 덮어쓸 위험이 있음.
  3. 사용자 입력값을 검증하지 않음
    → 입력값 크기 제한을 하지 않으면 예상보다 긴 문자열을 입력받아 취약점이 발생할 수 있음.

1.3 취약한 언어 및 환경

프로그래밍 언어 버퍼 오버플로우 영향
C / C++ 취약, 직접 메모리 관리 필요
Assembly 취약, 모든 메모리 접근 직접 조작
Rust / Java / Python 보안 강화됨, 자동 메모리 관리 지원

 

C/C++은 직접 메모리 관리를 수행해야 하며, 기본적으로 보안 기능이 부족하기 때문에 버퍼 오버플로우 공격의 주요 대상이 됩니다. 반면, Rust, Java, Python은 자동 메모리 관리와 경계 검사를 지원하여 취약점 발생 가능성이 낮다.

 

2. 주요 버퍼 오버플로우 공격 기법

2.1 스택 기반 버퍼 오버플로우 (Stack-Based Buffer Overflow)

개념

  • 프로그램이 스택에 할당된 버퍼 크기보다 더 큰 데이터를 입력받으면, 메모리 구조를 덮어씌울 수 있음.
  • 이 과정에서 함수의 리턴 주소(Return Address)를 덮어쓰면, 공격자가 원하는 위치로 프로그램 실행 흐름을 바꿀 수 있음.

예제 코드 (취약한 C 프로그램)

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

void vulnerable_function(char *input) {
    char buffer[64];  // 64바이트 크기의 버퍼
    strcpy(buffer, input);  // 버퍼 크기 검증 없이 복사 (취약점 발생)
}

int main(int argc, char *argv[]) {
    if (argc > 1) {
        vulnerable_function(argv[1]);
    }
    return 0;
}

공격자는 64바이트 이상의 입력값을 주입하여 메모리의 리턴 주소를 덮어쓸 수 있음

2.2 힙 기반 버퍼 오버플로우 (Heap-Based Buffer Overflow)

개념

  • 힙(Heap) 영역은 malloc()이나 new 같은 동적 메모리 할당 함수로 생성됨.
  • 버퍼 오버플로우가 발생하면 힙 메모리의 관리 정보(메타데이터)를 손상시키고, 공격자가 원하는 방향으로 메모리를 조작할 수 있음.

예제 코드 (취약한 C 프로그램)

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

int main() {
    char *buffer1 = malloc(64);
    char *buffer2 = malloc(64);
    
    strcpy(buffer1, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
    free(buffer1);
    return 0;
}

공격자는 buffer1의 크기를 초과하는 데이터를 입력하여 malloc() 메타데이터를 손상시킬 수 있음

2.3 Return Oriented Programming (ROP) 공격

  • ROP 공격은 DEP(실행 방지 메모리 보호 기법)를 우회하는 기술로, 기존의 메모리 보호 기능을 무력화함.
  • 실행 가능한 쉘코드를 직접 삽입하는 대신, 이미 존재하는 코드 조각(Gadget)을 체인 방식으로 연결하여 악의적인 동작을 수행.

 

3. 버퍼 오버플로우 방어 기법

3.1 안전한 코드 작성

취약한 코드 대신 안전한 함수 사용

// 취약한 코드
strcpy(buffer, input);

// 안전한 코드
strncpy(buffer, input, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';

경계 검사 수행

if (strlen(input) < sizeof(buffer)) {
    strcpy(buffer, input);
} else {
    printf("입력값이 너무 큽니다!\n");
}

3.2 실행 방지 기법

방어 기법 설명
Stack Canary 스택에서 리턴 주소 앞에 보호값을 넣고 변조 감지
DEP (Data Execution Prevention) 스택과 힙에서 코드 실행 차단
ASLR (Address Space Layout Randomization) 메모리 주소를 무작위로 배치하여 공격 어려움
Control Flow Integrity (CFI) 프로그램 실행 흐름 조작 차단

 

컴파일러 옵션 적용 예시

gcc -fstack-protector -o secure_program program.c
gcc -z noexecstack -o secure_program program.c

버퍼 오버플로우는 C/C++과 같은 저수준 언어에서 직접 메모리 관리를 수행해야 하는 환경에서 자주 발생하는 보안 취약점입니다. 그러나 현대 시스템에서는 Stack Canary, ASLR, DEP 등의 보호 기법이 도입되었으며, 이를 활용하면 공격을 어렵게 만들 수 있습니다.

반응형
Comments