Computer >> 컴퓨터 >  >> 프로그래밍 >> C++

C 언어 형식 문자열 취약점 완벽 정리: 예제 코드와 예방 방법

형식 문자열(Format String)이란?

형식 문자열(Format String)은 문자열을 서식화(formatting)하기 위해 사용되는 ASCII 문자열입니다. 일반 텍스트와 서식 지정 매개변수(예: %d, %s, %x 등)로 구성되며, C 언어에서는 프로그램의 출력 결과를 원하는 형태로 서식화하기 위해 다양한 형식 문자열이 활용됩니다.

대표적인 형식 문자열 함수로는 printf, sprintf, fprintf, snprintf 등이 있으며, 이들 함수는 첫 번째 인수로 형식 문자열을 받아 출력 형식을 결정합니다.

형식 문자열 취약점(Format String Vulnerability)이란?

형식 문자열 취약점은 프로그래머가 실수로 범하기 쉬운 코딩 오류에서 비롯되는 보안 버그입니다. 검증되지 않은 사용자 입력 데이터가 그대로 출력 함수의 형식 문자열 자리에 전달되면, 공격자는 이를 악용해 임의의 메모리 주소에 값을 읽거나 쓸 수 있습니다.

특히 %x, %p 같은 서식 지정자를 통해 스택 메모리의 내용을 유출할 수 있고, %n 서식 지정자를 사용하면 특정 메모리 주소에 값을 기록하는 것도 가능합니다. 이는 프로그램 크래시, 정보 유출, 심각한 경우 원격 코드 실행(RCE)으로 이어질 수 있습니다.

취약한 코드 예제

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

int main(){

    char buffer[100];
    strncpy(buffer, "Hii ", 5);
    printf(buffer);   // 취약함: 형식 문자열에 변수를 직접 전달

    return 0;
}

위 코드에서 printf(buffer)처럼 형식 문자열 인수에 변수를 직접 전달하는 것이 문제입니다. 만약 buffer에 %x%x%x와 같은 문자열이 들어 있다면, printf는 이를 서식 지정자로 해석하여 스택에 저장된 값들을 출력하게 됩니다.

안전한 코드 예제

printf("%s", buffer);   // 안전함: 형식 문자열은 상수로 고정

형식 문자열 취약점 예방 방법

형식 문자열 취약점을 방지하기 위해 다음과 같은 조치를 취할 수 있습니다.

  • 형식 문자열을 입력 데이터로 사용하지 않기: 형식 문자열은 반드시 프로그램 내부에 고정된 상수로 작성해야 하며, 사용자 입력을 형식 문자열 자리에 전달해서는 안 됩니다. 변수 문자열을 출력할 때는 "%s" 서식 지정자를 활용하면 간단하게 해결됩니다.

  • 상수 형식 문자열과 인수 분리: 형식 문자열은 항상 상수로 생성하고, 모든 변수 문자열은 상수 문자열에 포함시키지 말고 함수 호출의 인수로 별도로 전달해야 합니다. 즉, printf(str) 대신 printf("%s", str) 형태로 작성합니다.

  • 형식 가드(Format Guard) 사용: 상수 및 변수 문자열 초기화 규칙을 지킬 수 없는 특수한 경우에는 형식 가드 라이브러리를 활용하여 형식 문자열 인수를 강제로 검증함으로써 취약점 발생 가능성을 차단합니다.

  • 컴파일러 경고 활성화: GCC의 -Wformat -Wformat-security 옵션 등을 사용하면 컴파일 단계에서 위험한 형식 문자열 사용을 사전에 탐지할 수 있습니다.

마무리

형식 문자열 취약점은 간단한 코딩 습관만으로도 충분히 예방할 수 있는 보안 취약점입니다. 핵심 원칙은 형식 문자열은 항상 상수로, 변수 데이터는 인수로 전달하는 것입니다. 이 원칙을 준수하고 컴파일러 경고를 적극적으로 활용한다면 안전한 C 프로그램을 작성할 수 있습니다.