Proven by Intelligence
보이지 않는 안전을 인텔리전스로 증명하다.
기술 인사이트를 만나보세요.
CWE - 658/659 정복하기 (3)
l CWE-123: Write-what-where Condition
CWE-123은 공격자가 임의의 위치에 임의의 값을 Write(쓰기)할 수 있는 상태에 대한 위험을 말합니다.
버퍼 오버 플로우를 유발하는 주된 요인으로, 이외 CWE-134(Use of Externally-Controlled Format String), CWE-416(Use After Free), CWE-590(Free of Memory not on the Heap)과 같은 Memory Corruption 문제 또는 CWE-364(Signal handler Race Condition), CWE-476(Signal Handler Use of a Non-reentrant Function)과 같은 동시성(Concurrency) 문제를 유발하기도 합니다.

위 예제는 할당된 메모리에 데이터가 Overwrite 되는 문제가 발생하는 Write-what-where Condition의 대표적인 예제입니다.
공격자가 임의의 값 또는 악성 데이터를 시스템의 입력 값으로 주입하여 메모리 복사 함수인 strcpy() 호출 시 buf1의 범위를 넘어 데이터가 Write될 경우, buf2에 데이터가 Overwrite됩니다. 오버 플로우가 발생하여 시스템 오작동과 같은 실행에 이상이 발생하거나 악성 코드가 삽입되었을 경우에는 임의 코드 실행, 권한 설정과 같은 심각한 보안 문제가 발생할 수 있습니다.
이러한 문제를 예방하기 위해 개발자는 포인터나 배열 사용 시 정해진 범위 내 사용되도록 코드를 작성하고, 문자열 관련 라이브러리 함수 사용 시에는 정확한 크기의 데이터가 참조될 수 있도록 올바른 방식으로 함수를 사용해야 합니다.
ㅣ CWE-124: Buffer Underwirte ('Buffer Underflow')
CWE-124는 포인터나 인덱스가 버퍼의 시작 이전의 위치를 가리켜 데이터가 쓰여지는 문제를 말합니다.
흔히 Buffer Underrun 또는 Buffer Underflow으로 불리며, 일반적으로 포인터 연산으로 인해 유효한 메모리의 시작 이전 시점을 가리키거나, 음수의 인덱스가 참조될 때 발생합니다.
잘못된 메모리 접근은 Memory Corruption을 발생 시켜 시스템 충돌이나 임의의 코드를 실행시켜 오작동을 유발합니다.
심지어 또 다른 보안 서비스의 실행을 방해하는 요소가 되어 시스템의 무결성과 가용성, 보안성에 영향을 미치기도 합니다.
아래 예제 코드는 입력 받은 문자열 내 여백(스페이스)을 제거하는 작업을 수행하는 함수입니다.
함수는 입력 받은 문자열을 지역 포인터 변수인 message에 복사한 뒤, 문자열 끝에서부터 반대로 검토하여 여백을 NULL 문자로 덮어쓰는 반복 작업을 수행하고 있습니다.
그러나 입력된 문자열이 모두 여백일 경우 문제가 발생할 수 있습니다.
while 조건 구문이 문자열의 첫번째 위치까지 수행되면 len의 감소 연산으로 인해 len 값은 음수가 되고, 이후 다시 while 구문의 조건을 확인하기 위해 len 값을 통한 message 변수 접근 시 message 배열의 시작 이전의 위치에 참조되는 Buffer Underwrite가 발생하게 됩니다.
아래 코드는 입력된 문자열에 대한 모든 경우를 고려하지 않고 작성된 코드이며, while 구문의 조건을 수정하거나 인덱스인 len 변수에 대한 조건 구문을 추가하여 위험을 예방할 수 있습니다.

또는 프로그램 실행 시 포인터 연산이나 인덱스로 사용되는 모든 계산된 값에 대한 Sanity Check(새너티 검사, 연산의 결과가 참인지 평가하는 기본적인 테스트)를 수행하거나, 코드 작성 시 자동화 정적 분석 도구를 통해 올바르지 않은 index 참조나 포인터 연산을 검사하여 사전에 문제를 예방할 수 있습니다.
ㅣ CWE-125: Out-of-bounds Read
CWE-125는 소프트웨어가 의도된 버퍼의 이후 또는 시작 이전 즉, 정해진 범위 밖의 데이터를 읽는 문제를 말합니다.
이 문제는 주로 중요 정보 탈취나 시스템 crash를 발생하는 원인이 됩니다.
예를 들어, NUL 문자(\0)와 같은 센티널 값을 통해 데이터 읽기(Read) 작업을 중단하도록 조건을 설정했을 경우, 대량의 데이터 입력 시에 시스템 Crash가 발생할 수 있습니다.
데이터가 저장될 버퍼의 크기보다 더 큰 사이즈의 데이터가 입력된 후 센티널이 입력되는 경우, 버퍼 오버 플로우나 세그먼트 오류가 발생하기 때문입니다.

위 예제 코드는 특정 배열 위치에 저장된 값을 확인하는 함수입니다.
먼저, 배열의 최대 크기와 index 값을 비교하여 배열의 크기보다 작은 값의 index일 경우, index가 가리켜는 위치의 값을 return 합니다.
그러나 if 조건을 만족하지 않는 경우에는, 해당 index가 가리키는 위치의 값을 출력하고 value에 -1 값을 저장한 뒤 return을 수행합니다.
여기서 문제는 잘못된 조건입니다.
개발자는 배열이 최대 크기를 고려하여 if 구문의 조건을 수립하였으나, index 값이 음수일 경우에 대한 조건을 수립하지 않았습니다.
index 값이 음수라면 배열의 인덱스에 음수가 참조되고 Underwrite가 발생합니다.
이와 같은 문제를 회피하기 위해서는 입력 값에 대한 최대값 뿐만 아니라 최소값도 함께 검사하는 조건을 수행하도록 코드를 작성해야 합니다.
모든 입력은 잠재적인 위험을 보유하고 있기 때문에 개발자는 입력에 대한 유효성 검사를 조금 더 엄격하게 수행해야 할 필요가 있습니다.
예를 들어, 단순히 입력 값에 대한 유효성 검사를 수행하지 않고 요구 사항에 적합한 입력 값의 리스트를 사용하여 입력을 거부하거나 변환하는 등의 예외 처리를 수행하는 방안이 있습니다.
또한 입력의 유효성 검사 수행 시에는 크기나 타입 형, 입력된 데이터의 허용 가능한 크기나 범위, 누락 또는 추가 입력 등 관련된 모든 속성을 고려해야 합니다.
ㅣ CWE-126: Buffer Over-read / CWE-127: Buffer Under-read
CWE-126/127은 인덱스나 포인터와 같은 버퍼 접근 방식을 사용하여 타겟 버퍼의 이전 또는 이후 메모리 위치에 참조되는 문제를 말합니다.
위에서 설명한 CWE-125와 매우 유사하며, CWE-126/127은 CWE-125의 자식의 속성을 지니고 있어 큰 범주인 CWE-125에 포함되어 데이터 Over/Under-read 문제 중 인덱스나 포인터와 같은 센티널 값 또는 데이터의 길이에 영향을 미치는 입력으로 인해 발생하는 특정 유형의 문제를 명시하고 있습니다.
ㅣ CWE-128: Warp-around Error
CWE-128은 특정 타입의 최대 값을 초과하여 매우 작은 값이나 음수 또는 정의되지 않은 값으로 Wrapping 되는 문제를 말합니다.
예를 들어, unsigned integer(8비트)는 0(0000 0000)에서부터 255(1111 1111)까지 표현이 가능합니다. 여기에 1을 더하면 256이 되어 8비트로 표현이 불가하고 비트 수를 넘어 캐리(carry)가 발생하며 값은 0(0000 0000)이 됩니다. 이처럼 unsigned int 타입의 값에 계속해서 1을 더하면 254, 255, 0, 1, 2, 3 …이 되고 signed int 타입의 값에 계속해서 1을 더하면 125, 126, 127, -128, -127, -126 …이 되는 것과 같이 값이 순환되거나 의도하지 않은 값을 갖는 현상을 Wrap around라고 합니다.
이 약점은 의도하지 않은 값으로 인해 프로그램이 실행되어 시스템 오작동 또는 시스템 Crash 발생과 같은 가용성 문제와 더불어 데이터 변형과 같은 Memory Corruption을 발생 시켜 무결성 문제의 원인이 되기도 합니다.
이러한 오류를 예방하기 위해서는 개발 언어 선정부터 사용하는 프로토콜이나 데이터 타입 등의 최대/최소 값의 범위를 구체적으로 명시하고 요구사항 내 정의해야 합니다.
더불어 코드 구현 또는 실행 시, 값이 증가 되는 변수에 대하여 유효한 범위 내에서 계산이 정상 수행되는지 Sanity Check(새너티 검사)를 수행하는 것이 좋습니다.
출처 : CWE 웹사이트 (http://cwe.mitre.org/index.html)
