OPEN HYPER STEP
← 목록으로 (화이트햇 보안)
SECURITY · 91 / 96
security
CHAPTER 91 / 96
읽기 약 2
FUNCTION

버퍼 오버플로우: 왜 발생하는가


핵심 개념

⚠️ 교육 목적. strcpy의 위험 + 스택 버퍼 오버플로우 원리 + 리턴 주소 덮어쓰기 개념. 안전한 코드 비교.

본문

버퍼 오버플로우의 원리

📋 코드 (12줄)
스택 메모리 (높은 주소 → 낮은 주소):

[ saved %rbp ]
[ return addr ]   ← 함수 종료 시 점프할 주소
[ buffer[64]  ]   ← 사용자 입력 저장
[              ]
[ ↑ 입력 방향  ]   ← 작성하다 보면 위로 진행
[              ]

⚠️ 입력 65바이트면 saved %rbp 덮어씀
⚠️ 입력 73바이트면 return addr 덮어씀
   → 임의 코드 실행 가능 (전형적인 익스플로잇)

취약한 코드

C📋 코드 (24줄)
// ⚠️ 교육 목적. 허가된 환경에서만 실행하세요.
#include <stdio.h>
#include <string.h>

void vulnerable(const char *input) {
    char buffer[64];
    strcpy(buffer, input);  // ⚠️ 경계 검사 없음
    printf("입력: %s\n", buffer);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("사용: %s <input>\n", argv[0]);
        return 1;
    }
    vulnerable(argv[1]);
    return 0;
}

// 실행:
// $ ./vuln "Hello"               → 정상
// $ ./vuln "$(python3 -c 'print("A"*100)')"
//   → Stack smashing detected (-fstack-protector 활성 시)
//   → 또는 Segfault

안전한 코드 — 5가지 패턴

C📋 코드 (50줄)
#include <stdio.h>
#include <string.h>

// 패턴 1: snprintf (권장)
void safe_v1(const char *input) {
    char buffer[64];
    snprintf(buffer, sizeof(buffer), "%s", input);
    // 자동 잘림 + \0 종료
    printf("%s\n", buffer);
}

// 패턴 2: strncpy + 명시적 \0
void safe_v2(const char *input) {
    char buffer[64];
    strncpy(buffer, input, sizeof(buffer) - 1);
    buffer[sizeof(buffer) - 1] = '\0';
    printf("%s\n", buffer);
}

// 패턴 3: 사전 길이 검사
void safe_v3(const char *input) {
    char buffer[64];
    size_t len = strlen(input);
    if (len >= sizeof(buffer)) {
        fprintf(stderr, "입력이 너무 깁니다 (최대 %zu)\n",
                sizeof(buffer) - 1);
        return;
    }
    strcpy(buffer, input);  // 이제 안전
    printf("%s\n", buffer);
}

// 패턴 4: fgets — 줄바꿈 포함 여부 처리
void safe_v4(void) {
    char buffer[64];
    if (fgets(buffer, sizeof(buffer), stdin) == NULL) return;
    // \n 제거
    buffer[strcspn(buffer, "\n")] = '\0';
    printf("%s\n", buffer);
}

// 패턴 5: 동적 할당
char *safe_v5(const char *input) {
    size_t len = strlen(input);
    char *buf = malloc(len + 1);
    if (!buf) return NULL;
    memcpy(buf, input, len);
    buf[len] = '\0';
    return buf;  // 호출자가 free 책임
}

위험 함수 vs 안전 함수

📋 코드 (8줄)
| 위험 | 안전 |
|---|---|
| strcpy(d, s)       | strncpy / snprintf |
| strcat(d, s)       | strncat / snprintf |
| sprintf(d, ...)    | snprintf |
| gets(d)            | fgets |
| scanf("%s", d)     | scanf("%63s", d) (크기 제한) |
| memcpy(d, s, len)  | 사전 d 크기 검증 |

-fstack-protector 효과

C📋 코드 (12줄)
// 컴파일러가 자동 삽입하는 카나리 (canary)
// 함수 진입: 스택에 비밀 값 저장
// 함수 종료: 비밀 값 검사 → 변조 시 abort

// 컴파일
// gcc -fstack-protector-strong vuln.c -o vuln
// (모던 gcc는 기본 활성화)

// 오버플로우 시도 시:
// *** stack smashing detected ***
// terminated
// Aborted (core dumped)

ASan으로 정확한 진단

BASH📋 코드 (12줄)
gcc -fsanitize=address -g -O0 vuln.c -o vuln
./vuln "$(python3 -c 'print("A"*100)')"

# 출력:
# ==12345== ERROR: AddressSanitizer: stack-buffer-overflow
# WRITE of size 100 at 0x7fff... thread T0
#     #0 0x... in vulnerable vuln.c:7
#     #1 0x... in main vuln.c:14
#
# Address 0x7fff... is located in stack of thread T0
# at offset 100 in frame
#   #0 0x... in vulnerable vuln.c:5

실전 — 강건한 입력 처리

C📋 코드 (32줄)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_INPUT 1024

int read_safe_input(char *buf, size_t size) {
    if (size == 0) return -1;

    if (fgets(buf, (int)size, stdin) == NULL) return -1;

    size_t len = strlen(buf);
    if (len > 0 && buf[len-1] == '\n') {
        buf[len-1] = '\0';
    } else {
        // 입력이 너무 김 — stdin 비우기
        int c;
        while ((c = getchar()) != '\n' && c != EOF) {}
        return -1;
    }
    return 0;
}

int main(void) {
    char input[MAX_INPUT];
    if (read_safe_input(input, sizeof(input)) != 0) {
        fprintf(stderr, "입력 오류\n");
        return 1;
    }
    printf("입력: %s\n", input);
    return 0;
}

다음 챕터

CH.8 "포맷 스트링 취약점" — printf 잘못 쓰면 어떤 일이.


AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude

무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6

내 C 코드의 모든 strcpy/sprintf/gets/scanf
위치를 찾고 안전한 함수로 변환해줘.
ChatGPT

무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro

OWASP Buffer Overflow 분류와 실제
CVE 사례 5개를 코드 비교로 분석해줘.
Gemini

무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro

내 C 프로젝트에 fortify_source/stack-protector
적용 + ASan/MSan 통합 가이드를 만들어줘.
Grok

무료: Grok 4.1 / SuperGrok $30/mo

2026년 메모리 안전 언어(Rust/Zig) 채택률과
C/C++ 점유율 변화를 솔직히 알려줘.

⭐ 이것만 기억하세요
버퍼 오버플로우: 왜 발생하는가 이 3가지만 확실히 잡으세요
1.버퍼 오버플로우는 입력이 버퍼 크기를 넘어 인접 메모리를 덮어쓰는 현상
2.리턴 주소 덮어쓰기로 임의 코드 실행 가능 — 모든 strcpy/strcat/sprintf/gets 제거
3.다음 챕터 CH.8에서 포맷 스트링 — printf의 의외의 위험


공유하기
진행도 91 / 96