Buffer Overflow , 내가 처음 만들고/3가지 공격 유형/4가지 방어한 유형 보안 취약점

버퍼 오버플로우(Buffer Overflow)는 제가 처음으로 시도해본 공격이자, 동시에 처음으로 방어하는 방법을 배운 보안 취약점입니다. 요즘은 메모리를 안전하게 관리해주는 언어들이 많아졌지만, 이 개념을 이해하고 있으면 다른 분야에서도 큰 도움이 됩니다.


이 글의 범위: 개념 정리와 가장 작은 예제 코드 중심의 입문 개요입니다. 제가 처음 시도했던 실습의 디버거 출력 같은 과정은 이 글에 담지 않았습니다.

Buffer Overflow란?

버퍼 오버플로우는 프로그램이 메모리 버퍼에 할당된 크기보다 더 많은 데이터를 쓸 때 발생합니다. 주로 C나 C++처럼 메모리 관리를 직접 해야 하는 언어에서 자주 발생하죠. 공격자는 이 취약점을 악용해 프로그램 흐름을 바꾸거나, 악성 코드를 실행할 수 있어 보안상 매우 심각한 이슈입니다.


Buffer Overflow

주요 공격 유형

1. 스택 기반 오버플로우

  • 지역 변수 버퍼를 넘쳐서 리턴 주소를 덮어씁니다.
  • 쉘코드를 삽입하고 실행 흐름을 탈취할 수 있습니다.

2. 힙 기반 오버플로우

  • 동적으로 할당된 메모리를 공격합니다.
  • 함수 포인터나 객체 구조를 변경할 수 있습니다.(OWASP)

3. 포맷 스트링 공격

  • %s, %x 같은 포맷 문자열을 악용합니다.
  • 메모리를 임의로 읽거나 쓸 수 있습니다.

예시 코드 (C)

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

void vulnerable_function() {
    char buffer[10];
    printf("Enter input: ");
    gets(buffer); // 위험! 길이 제한 없음
    printf("You entered: %s\n", buffer);
}

int main() {
    vulnerable_function();
    return 0;
}

설명:

위 코드에서 buffer는 10바이트만 저장할 수 있지만, gets()는 입력 길이를 확인하지 않습니다. 따라서 10자 이상 입력하면 인접한 메모리를 덮어쓸 수 있고, 심하면 리턴 주소까지 바뀔 수 있습니다. 실제 공격에서는 쉘코드를 삽입하거나 프로그램 흐름을 완전히 탈취할 수 있습니다.

gets()가 위험한 이유: C11 표준에서 삭제된 함수이고, 최신 컴파일러는 사용하면 경고를 냅니다. 아래는 버퍼 크기를 인자로 받는 fgets()로 바꾼 버전입니다.

#include <stdio.h>

void safe_function() {
    char buffer[10];
    printf("Enter input: ");
    if (fgets(buffer, sizeof(buffer), stdin) != NULL) { // 최대 9글자 + '\0'까지만 읽음
        printf("You entered: %s\n", buffer);
    }
}

int main() {
    safe_function();
    return 0;
}

fgets()는 최대 9글자와 종료 문자 '\0'까지만 저장하고, 더 긴 입력은 잘리며 남은 문자는 stdin에 남습니다.

취약한 버전을 실행하면: 많은 리눅스 배포판의 gcc는 스택 보호 기능이 기본으로 켜져 있어서, 긴 입력을 주면 리턴 주소가 탈취되는 대신 *** stack smashing detected *** 메시지와 함께 종료되는 경우가 많습니다. 아래 ‘방어 방법’의 스택 카나리가 동작한 결과입니다(배포판과 컴파일러 옵션에 따라 다를 수 있습니다).


방어 방법

1. 안전한 함수 사용

  • gets() 같은 위험한 함수 대신 fgets()를 사용합니다. fgets()는 버퍼 크기를 인자로 받아 그 이상은 쓰지 않습니다. sprintf() 대신 snprintf()를 쓰는 것도 같은 원리입니다.

2. 스택 카나리(Stack Canary)

  • 스택 오버플로우를 탐지하는 기법으로, 스택에 감시용 값을 삽입해 함수가 리턴하기 직전에 변경 여부를 확인하고, 바뀌었으면 프로그램을 중단합니다. gcc에서는 -fstack-protector-strong 옵션입니다.

3. ASLR(주소 공간 배치 무작위화)

  • 메모리 주소를 무작위로 배치하여 공격자가 예측하기 어렵게 만듭니다. 주소가 노출되면 우회될 수 있어서 ASLR만으로 완전한 방어는 아닙니다.

4. NX 비트 설정

  • 실행 불가능한 메모리 영역을 설정하여, 코드 실행을 방지합니다. 리턴 지향 프로그래밍(ROP) 같은 우회 기법이 있어서 다른 방어와 함께 사용합니다.

최신 동향 (2025년 기준)

2025년에도 버퍼 오버플로우는 여전히 심각한 보안 위협으로 간주됩니다. 미국 사이버 보안 및 인프라 보안국(CISA)과 FBI는 제조업체들에게 안전한 소프트웨어 개발 관행을 채택하여 버퍼 오버플로우 취약점을 제거할 것을 권고하고 있습니다. (Industrial Cyber)

또한, 최근에는 Fortinet 제품에서 발견된 스택 기반 버퍼 오버플로우 취약점(CVE-2025-32756)이 보고되었으며, 원격에서 인증되지 않은 공격자가 임의의 코드를 실행할 수 있는 심각한 문제로 평가되었습니다. (Integrity360)


마무리 생각

버퍼 오버플로우는 겉보기엔 단순한 프로그래밍 실수처럼 보이지만, 시스템 전체를 장악할 수 있는 심각한 취약점입니다. 요즘은 메모리 관리를 자동으로 해주는 언어를 많이 쓰니까 크게 신경 쓰지 않을 수도 있겠지만, 만약 임베디드나 저수준 프로그래밍을 하게 된다면 꼭 알아둬야 할 개념입니다.


키워드: 버퍼 오버플로우, Buffer Overflow, 스택 오버플로우, 힙 오버플로우, 포맷 스트링 공격, 보안 취약점, C 언어, 메모리 안전, 스택 카나리, ASLR, NX 비트, CISA, CVE-2025-32756


참고자료:

Integrity360: CVE-2025-32756 Advisory

CISA: Eliminating Buffer Overflow Vulnerabilities

By Mark