System Hacking/Basics

메모리를 지키는 방법: C/C++ 포맷 스트링 취약점과 버퍼 오버플로우

hanbunny 2025. 3. 12. 16:13

포맷 스트링

프로그래밍에서 텍스트의 형식을 지정하는 데 사용되는 문자열이다.

포맷 스트링은 일반 텍스트와 특수 형식 지정자(format specifiers)로 구성됨.

형식 지정자는 '%'로 시작하고 출력할 데이터의 유형과 형식을 지정한다.

 

그 중에도 버퍼 오버플로우 공격에 취약한 주요 포맷스트링은 이렇다.


위험 함수들

  • strcpy(dest, src):
    • 대상 버퍼(dest)의 크기를 확인하지 않고 소스 문자열(src)을 복사.
    • src가 dest보다 크면 오버플로우가 발생.
  • strcat(dest, src):
    • 대상 문자열(dest)의 끝에 src를 추가하지만 결과 문자열이 버퍼 크기를 초과하는지 확인하지 않음.
  • sprintf(buffer, format, ...):
    • 형식화된 문자열을 버퍼에 기록하지만 버퍼 크기를 검사하지 않음.
    • 결과 문자열이 버퍼보다 크면 오버플로우가 발생.
  • gets(buffer):
    • 사용자 입력을 버퍼에 읽지만 크기 제한 없이 읽어들임.
  • scanf("%s", buffer):
    • %s 지정자를 사용할 때 버퍼 크기를 제한하지 않으면 오버플로우가 발생 가능.

 

이런 특징때문에 버퍼 크기를 검사하고, 크기 제한이 있는 함수를 사용해야 보안이 강해짐.

그 대안으로 아래와 같은 함수들이 있다.

 

대안 함수들

 

  • strncpy(dest, src, n):
    • 최대 n바이트까지만 복사하여 버퍼 오버플로우를 방지합니다.
    • 주의: 자동으로 널 종료 문자를 추가하지 않을 수 있으므로 명시적으로 추가해야 합니다.
  • strncat(dest, src, n):
    • 최대 n바이트까지만 추가하여 버퍼 오버플로우를 방지합니다.
  • snprintf(buffer, size, format, ...):
    • 버퍼 크기를 지정하여 그 이상 기록되지 않도록 합니다.
  • fgets(buffer, size, stdin):
    • gets() 대신 사용하며, 최대 크기를 지정하여 안전하게 입력을 받습니다.
  • memcpy(dest, src, n):
    • 정확히 n바이트만 복사합니다. 크기를 명시적으로 지정해야 합니다.

 


포맷 지정자?

포맷 지정자(Format Specifiers)는 C/C++ 언어에서 printf, scanf와 같은 함수에서 데이터의 입출력 형식을 지정하는 데 사용된다.

포맷 지정자 자체가 직접적으로 버퍼 오버플로우를 발생시키지는 않지만,

포맷 지정자를 사용하는 함수의 특성과 사용 방식이 버퍼 오버플로우를 일으킬 수 있다.

 

 

  • %d 또는 %i: 정수(int)
  • %u: 부호 없는 정수(unsigned int)
  • %f: 실수(float, double)
  • %c: 문자(char)
  • %s: 문자열(string)
  • %p: 포인터 주소(pointer)
  • %x 또는 %X: 16진수(hexadecimal)
  • %%: % 기호 자체