Table of Contents
이 문서는 C 프로그래밍의 가장 지속적이고 위험한 보안 취약점 중 하나가 남아 있습니다. 수십 년 동안 잘 문서화 된에도 불구하고 데이터 손상, 시스템 충돌 및 원격 코드 실행과 같은 심각한 문제를 일으킬 수 있습니다. 안전한 C 코드를 작성하면 버퍼 오버 플로우가 발생하고 그(것)들을 방지하는 분야의 접근 방식이 깊은 이해가 필요합니다. 이 문서는 강력한 쓰기, 과잉 방지 C 코드, 기본 개념, 안전한 기능, 검증, 현대 보호 및 기본 보호 기법을 포함하는 포괄적 인 가이드를 제공합니다.
버퍼 오버플로우 이해
버퍼 오버플로는 버퍼보다 더 많은 데이터를 메모리 (버퍼)의 연속 블록에 기록 할 때 발생합니다. 버퍼가 스택 또는 힙 메모리에 떠오르기 때문에 경계를 초과하는 경계를 초과하는 인접한 메모리 위치를 초과합니다. 이 손상은 프로그램 상태를 변경할 수 있으며, 예측 가능한 행동을 도입하거나 공격자가 주사하고 실행하는 임의 코드를 적용 할 수 있습니다.
결과가 지나치게 얻는 것에 달려 있습니다. 스택의 반품 주소를 강조하면 공격자가 통제되는 코드로 실행을 리디렉션할 수 있습니다. 과잉 포인터는 임의 메모리 쓰기로 이어질 수 있습니다. 간단한 충돌은 denial-of-service 공격에 레버리지가 될 수 있습니다. 기계공학에 대한 이해는 예방을 위한 첫 단계입니다.
Stack 기반 오버플로
버퍼가 내부 함수를 선언한 로컬 변수는 스택에 저장됩니다. 스택은 리턴 주소, 저장된 프레임 포인터 및 기타 제어 데이터를 보유합니다. 선형 버퍼가 와 같은 경우, 리턴 주소와 그 이상의 데이터 유출이 발생합니다. Morris 웜 (1988)과 같은 클래식 악용은 멸균된 액세스를 얻기 위해 스택 오버플로를 사용했습니다.
Heap 기반 오버플로
역학적으로 할당 된 버퍼 (], 등) 힙에 남아. 여기 과잉 메타 데이터는 할당에 의해 사용, 힙 스프레이 또는 사용 후 무료 공격을 통해 충돌 또는 악용에 선도 할 수 있습니다. Heap 과잉은 악화하지만 위험한 더 어렵습니다.
일반적인 취약한 기능 및 그들의 안전한 대안
C 표준 라이브러리는 경계 검사를 수행하지 않는 여러 기능을 제공합니다. 이를 사용하여 버퍼 오버플로의 가장 일반적인 원인입니다. 더 안전한 카운터 파트로 대체하는 것은 기본적인 모범 사례입니다.
문자열 복사 및 Concatenation
- - 안전하지 않음: NULL 용어, 길이 제한까지 복사.
안전 대안:] - 대부분의 n 문자에 복사; 소스가 n보다 더 길어지 않다면 n이 아닌 용어를 사용하지 않는다면, 그래서 항상 수동으로 null-terminate. - 더 나은 아직: — BSD 및 많은 리눅스 시스템에 사용할 수; 항상 null-terminates 및 truncation 탐지에 대 한 소스 문자열의 길이를 반환.
- - 안전하지 않은: 경계없이 concatenates.
]안전 대안: ] - 대부분의 n 문자에 부과하고 항상 null-terminate.
산출과 입력을 체재하는
- - 안전하지 않은: 크기 검사 없이 버퍼에 포맷된 출력을 작성한다.
안전 대안:] ] - 크기 1 문자와 null 용어로 출력한 제한. - - 비슷한 위험; 대신 ]를 사용하십시오.
- - 극적으로 위험한; C11 표준에서 제거. 대신 ]를 사용하십시오.
- - 경계 체크 없음. 필드 폭 사양으로 ] 또는 를 사용하십시오.
메모리 복사 및 이동
- - n이 대상 버퍼 크기를 초과하지 않는 경우 안전.
안전 대안: ] (손잡이) 항상 n ≤ dest 크기를 보장합니다. - 일부 플랫폼은 Annex K (C11)에서 를 제공하지만 채택은 제한됩니다.
검증 및 크기 관리
안전 기능으로, 당신은 입력 길이를 유효하게 하고, 적당한 완충기 크기를 지키고, 잠재적인 truncation를 우아한 취급합니다.
입력 길이를 확인
외부 입력(사용자 입력, 네트워크 데이터, 파일 내용)을 복사하거나 처리하기 전에 최대 허용 길이를 결정하고 이를 초과하는 데이터를 거부하거나 truncate합니다. 예를 들어:
#define MAX_INPUT 255
char buffer[MAX_INPUT + 1]; // +1 for null
if (strlen(user_input) > MAX_INPUT) {
// Handle error: reject or truncate
fputs("Input too long", stderr);
return -1;
}
strncpy(buffer, user_input, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
Known Limits를 사용한 고정 크기 버퍼
가능한 한, 일정한 크기로 버퍼를 정의하고 코드를 통해 시행. 큰 크기가 공급되는 경우 스택 오버플로를 일으킬 수있는 가변 길이 배열 (VLA)를 피하십시오. 대신, 명시적 크기 체크와 동적으로 할당합니다.
처리 Truncation Explicitly
와 같은 기능 및 은 데이터를 truncate 할 수 있습니다. truncation을 감지하고 truncated 데이터가 허용되거나 오류가 발생하면 결정하는 반환 값의 인식이 될 수 있습니다. truncation을 무시하면 예상치 못한 상태에 버퍼를 떠날 수 있습니다.
Compiler Security Flags 및 Runtime Protections를 준수합니다.
현대 컴파일러는 코드 변경 없이 버퍼 오버플로우 탐지 및 완화를 추가하는 플래그를 제공합니다. 빌드 시스템에서 사용 가능.
- ] / ] - 반환 주소 전에 스택캐나다 (random 값)을 삽입합니다. 버퍼 오버플로가 반환 주소를 수정하기 전에 캐러리를 덮으면, 이 프로그램은 악용이 완료되기 전에 낙관합니다.
- ] - ]와 같은 안전 기능에 전화를 대체하고 대상 버퍼가 너무 작으면 체크 된 버전으로. 또는 더 높은 최적화를 필요로한다.
- - 과잉 플로우 또는 정보 누출을 버퍼 할 수있는 형식 문자열 취약점에 대한 경고.
- - 주소샌타이저(ASan) 악기 코드 버퍼 오버플로우, 사용 후 무료 및 실행시 다른 메모리 오류를 감지합니다. 느린 실행하지만 테스트에 대한 불가결입니다.
- - 멈춘 멈춘 횟수 (주의와 사용)를 방지합니다.
운영 체계 보호
스택캐나다는 한 층입니다. 현대 OS의 폭발 완화 기술이 포함:
- 데이터 실행 방지 (DEP) / NX 비트 - 마크 스택과 힙합을 비 컴파일할 수 있는, 쉘코드 실행을 방지.
- 주소 공간 레이아웃 랜덤화 (ASLR) - 대상을 예측하기 위해 더 열심히 만들기 위해 메모리 주소 (정상, 헬리콥터, 공유 라이브러리)를 무작위화합니다.
- Relocation Read-Only (RELRO) - 오버위트에서 GOT (Global Offset Table)를 보호합니다.
이러한 보호 (보통 기본)은 악용을위한 바를 제기하지만 안전한 코딩을 대체하지 않습니다.
Code Audit 및 정적 분석
자동화된 정적 분석과 결합된 인간 검토는 초기 버퍼 오버플로우 문제를 잡을 수 있습니다. 이러한 개발 워크플로우에 통합합니다.
- 수동코드 검토 - 버퍼 경계를 넘어 쓰기의 안전한 기능, 누락된 크기 체크 및 루프를 사용하는 것을 찾습니다.
- Static analysis tools - ]], ], , ]는 잠재적 과잉을 감지하고, 위험한 기능의 사용, 그리고 오프-원 오류. 그들은 CI 파이프라인에서 실행할 수 있습니다.
- Fuzzing - libFuzzer, AFL, 또는 기타 fuzzers를 사용하여 과잉 흐름을 유발할 수 있는 예상치 못한 데이터로 입력 처리.
보안 코드의 실제 예
Bounds Checking을 통한 안전한 문자열 복사
#include <stdio.h>
#include <string.h>
int safe_string_copy(char *dest, size_t dest_size, const char *src) {
if (!dest || !src || dest_size == 0) {
return -1; // Invalid parameters
}
size_t src_len = strlen(src);
if (src_len >= dest_size) {
// Source too large; truncation or error
// Option: copy what fits and null-terminate
strncpy(dest, src, dest_size - 1);
dest[dest_size - 1] = '\0';
return 1; // Truncation occurred
}
strncpy(dest, src, dest_size);
// strncpy fills remaining with null, so dest_size fits; no need to null-terminate if src shorter
return 0; // Success, no truncation
}
버퍼 크기에 대한 안전한 Integer 처리
버퍼 오버플로우는 크기 계산시 정수 오버플로우에서 발생할 수 있습니다. 항상 할당 전에 arithmetic을 확인합니다.
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
void *safe_malloc_array(size_t nmemb, size_t size) {
if (nmemb == 0 || size == 0) {
return NULL; // Or handle zero-size allocation
}
if (nmemb > SIZE_MAX / size) {
// Integer overflow would occur
errno = ENOMEM;
return NULL;
}
return malloc(nmemb * size);
}
Formatted Strings에 대한 snprintf 사용
char log_message[256];
int ret = snprintf(log_message, sizeof(log_message),
"User %s logged in from %s", username, ip_address);
if (ret < 0) {
// Output error
} else if ((size_t)ret >= sizeof(log_message)) {
// Truncation occurred; handle if needed
}
더 많은 모범 사례
- 버퍼 - 항상 불연성 메모리를 누출하는 것을 피하기 위해 버퍼를 제로합니다.
- 언바운드 깊이 - 스택 오버플로우가 깊은 재순환에서 발생할 수 있습니다. 이 반복 또는 한계 깊이를 사용하십시오.
- ] 쿼터] - 컴파일러가 최적화되어 앨리싱 문제를 직접 막지 못하게 할 수 있도록 도와줍니다.
- Prefer -correctness] - 입력 문자열의 사고 수정을 방지하고 의도를 시행합니다.
- Implement error handling - ], ], 등 함수의 반환값을 무시하지 마십시오.
더 많은 학습 자료
- SEI CERT C 코딩 표준 - C 코딩을 위한 종합적인 규칙.
- CWE-120: 입력의 크기 체크 없이 버퍼 복사] — 버퍼 오버플로 약점의 분류.
- OWASP 버퍼 오버플로 - 오픈 웹 응용 프로그램 보안 프로젝트의 실제지도.
- GNU C Library Manual: String and Array Utilities - 안전한 문자열 기능에 대한 문서.
- AddressSanitizer - 빠른 메모리 오류 탐지기.
관련 기사
C의 버퍼 오버플로는 선택적이지 않습니다. 그것은 언어와 함께 작업하는 개발자의 기본 책임입니다. 과잉 플로우의 메커니즘을 이해함으로써, 위험한 기능을 안전하고 대체, 엄격하게 검증된 입력 및 크기, 컴파일러 보호 활성화, 그리고 정적 분석 및 테스트를 고용하면 이러한 취약점의 위험을 극적으로 줄일 수 있습니다. 단일 기술이 충분하지 않습니다; 깊이의 방어 - 코딩 분야, 컴파일러 플래그, OS M, 스테이너의 보안, 그리고 지속 가능한 코드의 보안을 제공 할 수 있습니다. 이러한 취약점은 강력한 보안을 제공 할 수 있습니다.