설명
printf 계열 함수는 형식 인자의 퍼센트 지시문을 해석하고 각 지시문에 필요한 값을 뒤따르는 인자에서 읽습니다. 신뢰할 수 없는 입력을 형식 인자로 전달하면 공격자가 %x, %s, 위치 지정 인자 등의 지시문을 주입해 프로세스 메모리를 읽거나 잘못된 인자 접근으로 프로세스를 중단시킬 수 있습니다. %n 지시문은 지금까지 출력한 문자 수를 인자가 가리키는 메모리에 기록하므로 메모리 변조로 이어질 수도 있습니다.
출력 길이를 제한하는 snprintf()도 형식 문자열 자체를 안전하게 만들지는 않습니다. 또한 POSIX의 syslog()는 메시지 본문을 printf와 같은 방식으로 생성하므로 메시지 인자에도 같은 원칙이 적용됩니다.
잠재적 영향
- 프로세스 메모리와 주소 등 민감한 정보가 노출될 수 있습니다.
- 잘못된 메모리 접근이나 과도한 출력으로 프로세스가 중단될 수 있습니다.
- 실행 환경과 뒤따르는 인자 상태에 따라 메모리 변조 또는 프로세스 권한으로의 코드 실행으로 이어질 수 있습니다.
해결 방법
- 형식 문자열을 코드의 리터럴로 고정하고 신뢰할 수 없는 텍스트는
%s같은 일반 값 인자로 전달합니다. - 버퍼 출력 함수와
va_list변형, 시스템 로그에도 같은 원칙을 적용합니다. - C++23을 준수하는 구현에서 직접 텍스트를 출력한다면 리터럴 형식을 사용하는
std::print("{}", input)을 사용할 수 있습니다. 스트림 출력이 목적에 맞는 경우std::cout << input도 퍼센트 지시문을 형식으로 해석하지 않습니다. sanitize_format()같은 함수 이름이나%문자 치환만으로 안전하다고 간주하지 않습니다. 고정 형식을 사용하고 지시문에 맞게 인자의 개수와 형식도 확인합니다.- GCC의
-Wformat=2같은 형식 진단을 활성화해 실수를 추가로 탐지합니다. 컴파일러 경고는 고정 형식 문자열을 대신하는 해결책이 아닙니다.
예시
표준 출력
변경 전
c
#include <stdio.h>
int main(int argc, char **argv) {
if (argc != 2) {
return 2;
}
printf(argv[1]);
return 0;
}
argv[1]의 퍼센트 지시문이 형식 문법으로 해석됩니다.
변경 후
c
#include <stdio.h>
int main(int argc, char **argv) {
if (argc != 2) {
return 2;
}
printf("%s", argv[1]);
return 0;
}
형식은 리터럴로 고정되고 argv[1]은 문자열 값으로만 처리됩니다.
C++의 std::printf()
cpp
#include <cstdio>
void show_user_input(const char *input) {
// 취약: input이 형식 문자열로 해석됩니다.
std::printf(input);
// 안전: input은 %s의 값 인자입니다.
std::printf("%s", input);
}
std:: 네임스페이스를 사용해도 C 스타일 형식 문자열의 의미는 바뀌지 않습니다. C++23의 std::print()를 사용할 때도 std::print("{}", input)처럼 형식을 리터럴로 유지해야 합니다.
시스템 로그
c
#include <syslog.h>
void log_user_input(const char *input) {
/* 취약: input이 형식 문자열로 해석됩니다. */
syslog(LOG_NOTICE, input);
/* 안전: input은 %s의 값 인자입니다. */
syslog(LOG_NOTICE, "%s", input);
}
참조
- POSIX.1-2024
fprintf()계열 - POSIX.1-2024
vfprintf()계열 - POSIX.1-2024
syslog() - ISO/IEC 14882:2024 Programming languages — C++
- Microsoft C++ 표준 라이브러리
<cstdio> - GCC 16.1 libstdc++
<cstdio>구현 - GCC 16.1 libstdc++ C++23
<print>구현 - SEI CERT C FIO30-C: Exclude user input from format strings
- GCC 형식 문자열 경고 옵션
- CWE-134: Use of Externally-Controlled Format String