Proven by Intelligence
보이지 않는 안전을 인텔리전스로 증명하다.
기술 인사이트를 만나보세요.
CWE - 658/659 정복하기 (5)
l CWE-170: Improper Null Termination
CWE-170은 문자열이나 배열이 Null 문자(\0)와 같은 종단 문자로 끝나지 않는 문제를 말합니다. 이 문제는 주로 정해진 범위를 벗어나 데이터가 쓰이는 오버 플로우로 인해 발생하거나 strncpy() 와 같은 문자열 복사 함수를 잘못 사용할 때 발생합니다. 종단 문자를 사용하지 않을 경우 다양한 문제가 발생하는데 가장 큰 문제는 보안입니다. 버퍼 오버 플로우나 데이터 깨짐 현상 등으로 인해 의도하지 않은 방식으로 SW가 실행되거나 심지어는 중요 정보가 노출되기도 합니다.

<코드 수정 후>

위 코드는 악용될 가능성은 없으나 안전한 함수가 사용되었음에도 불구하고 널문자가 생략되거나 잘못 배치될 수 있음을 보여주는 좋은 예시입니다. 프로그램 실행 후 출력 결과는 다음과 같습니다. "The last character in shortString is: n (6e)"
출력되는 결과를 보면 shortString 배열 마지막 위치의 값은 널문자가 아닌 복사 받은 문자열 중 하나의 문자입니다. shortSting변수에 저장된 문자열은 끝을 구분할 수 없기 때문에 이후 새로운 메모리에 복사되거나 참조되는 등의 작업이 수행될 때 올바른 문자열을 사용할 수 없게 됩니다.
이처럼 복사할 데이터의 크기를 명시하는 흔히 말해 안전한 함수를 사용했음에도 불구하고 문자열의 끝이 종단 문자로 종료되지 않는 문제가 발생할 수 있습니다. 예시 코드 내 발생하는 문제를 회피하기 위해서는 복사 받는 shortSting 문자의 크기에서 종단 문자를 제외한 크기만큼 데이터 복사가 이루어질 수 있도록 사이즈 인자를 조절하는 코드가 필요합니다.
문자열의 끝을 표기해주는 종단 문자의 사용은 하나의 약속입니다. 개발자들은 문자열 또는 배열 사용 시 종료 문자를 입력하는 코드를 반드시 삽입하는 규칙을 정하고 문자열 복사 함수를 사용할 때 널 문자가 입력되는지 확인할 수 있는 조건을 추가하여 사전에 문제를 예방할 수 있는 방안을 활용해야 합니다.
l CWE-188: Reliance on Data/Memory Layout
CWE-188은 프로토콜이나 데이터 구조에 대한 구성 방식을 잘못 가정함으로 인해 의도하지 않은 프로그램 동작이 발생하는 문제를 말합니다. 플랫폼이나 프로토콜 버전을 변경할 때, 의도하지 않은 방식으로 데이터의 메모리 구조가 변경될 수 있습니다. 예를 들어 프로토콜 버전에 따라 데이터에 서로 다른 크기의 패딩(Padding)이 추가되거나 오프셋 계산이 다르게 수행되어 데이터 레이아웃이 의도하지 않은 방식으로 이루어질 수 있습니다. 이는 의도하지 않은 필드에 접근하거나 다른 형식의 데이터 타입을 사용하게 되는 문제로 이어지게 됩니다.

위 예제는 char변수 a 선언 후 a의 주소 값에 1을 더한 위치에 b가 존재한다고 가정하고 있습니다. 이후 파생된 주소를 통해 변수 b에 0 값을 저장하고 있습니다. 그러나 변수 b는 a의 1 바이트 바로 뒤에 위치하지 않을 수도 있고 또는 a 보다 1바이트 앞에 위치할 수도 있습니다. 혹은 32비트 환경일 경우 2개의 변수 사이에는 3 바이트가 존재할 수도 있습니다.
이처럼 데이터 구조에 대한 잘못된 가정은 올바르지 못한 위치에 알 수 없는 데이터를 저장시켜 이로 인해 시스템 오작동을 유발할 수 있습니다. 이 취약점은 설계나 디자인 단계에서 사용할 프로토콜 또는 플랫폼 버전과 레이아웃을 명확하게 지정하여 사전 문제를 회피할 수 있으며 또는 개발된 프로그램을 프로토콜 환경과 문법에 따라 정상 동작하는지 테스트를 수행하여 검사하고 예방할 수 있습니다.
l CWE-191: Integer Underflow (Wrap or Wraparound)
CWE-191은 값의 뺄셈 연산으로 인해 정수의 최소 값보다 작은 값이 발생하여 예상하지 못한 값이 생성되는 문제를 말합니다. “Integer Underflow”는 주로 Singed 정수의 뺄셈 결과로 음수가 되어 부호(Singned/Unsigned) 문제가 발생할 때를 의미합니다. 그러나 Unsigned 즉, 부호가 없는 정수의 잘못된 뺄셈으로도 언더 플로우 문제가 발생할 수 있어, Signed 정수에서만 발생하는 이슈라고 단정할 수 없습니다. 또한 “Integer Underflow”는 어는 배열의 인덱스가 음수일 경우에 대한 문제를 말하기도 합니다. 이 취약점은 버퍼 오버 플로우를 유발하거나 의도하지 않은 값으로 인해 프로그램의 오작동 및 충돌이 발생할 수 있습니다.
아래 예제는 32비트 Singed int 타입의 뺄셈을 수행하는 코드입니다. Signed int 타입의 범위는 -2,147,483,648 ~ 2,147,483,647 입니다. Signed int 타입의 변수 i는 범위 내 가장 작은 값을 저장하고 있지만 1을 빼는 연산을 수행하면 i의 값은 -2147483649가 아닌 2147483647이 됩니다. 이처럼 부호가 Wraparound되며 Integer Underflow가 발생합니다.

※ 참고 : CWE-128: Warp-around Error
이 문제는 의도하지 않은 임의의 코드를 실행시켜 프로그램 오작동을 유발합니다. 이처럼 가용성과 무결성 문제와 더불어 접근 제한에 대한 문제를 발생시켜 보안에 취약한 SW를 만들기도 합니다. 이러한 문제를 예방하기 위해서는 프로그램에 사용되는 모든 데이터에 대한 정확한 요구사항을 명시하고 각 데이터를 저장할 수 있는 범위의 올바른 자료형을 사용할 수 있도록 설계하는 것이 중요합니다.
알려진 취약점 리스트인 CVE에 따르면, 2004년 10월 21일 Linux 운영체제(2.6.8 이전 버전) IPTABLE(패킷 필터링 도구)의 방화벽 로깅 규칙에서 정수 언더 플로우가 확인되어 취약한 IP 패킷을 통해
DoS(Denial Of Service), 어플리케이션 충돌을 일으킬 수 있다는 내용이 발표되었습니다. 예를 들면, 방화벽이 활성화되어져 있을 때 공격자가 조작된 IP 패킷을 전송시키면 시스템을 중단시킬 수 있는 심각한 보안 문제입니다. 이는 2004년 8월 25일부터 알려진 취약점인 CVE-2004-0816으로 기록되었으며, 대표적인 정수 언더 플로우 발생 사례로 기록되어 있습니다. 또한 공개적으로 악용될 가능성이 있어 취약한 코드에 대한 기술적인 세부 사항은 알려지지 않았지만 이후 리눅스 커널의 패치 버전 업그레이드를 통해 해당 문제를 해결했다고 보고됩니다.
(참고: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2004-0816)
l CWE-192: Integer Coercion Error
CWE-192는 원시 자료형의 형 변환, 확장, 절단 등과 관련된 결함들을 말합니다. 이는 데이터의 가용성 또는 무결성 문제를 주로 유발하지만 경우에 따라 보안과 관련된 결함의 원인이 되기도 합니다. 예를 들어, 잘못된 형 변환은 저장된 데이터의 절단으로 인해 값 변형을 유발하여 무결성 이슈가 발생할 수 있으며, 버퍼 오버 플로우 또는 언더 플로우가 발생하여 임의의 코드를 실행하는 등 가용성 및 보안 이슈가 발생할 수 있습니다.
C 언어에서는 개발자가 형 변환을 직접 지정하는지 또는 컴파일러가 암시적으로 수행하는지에 따라 "Coercion"과 "Casting"의 의미가 상이합니다. 필요에 따라 암묵적으로 자료형이 변경되는 것을 묵시적 형 변환(Coercion) 이라고 하며 개발자가 직접 변경하는 것을 명시적 형 변환(Casting)이라고 합니다. 묵시적인 방법으로 타입을 변경할 경우 변경된 데이터 타입의 표현 범위를 벗어나 데이터의 일부가 누락되는 등 오버 플로우가 발생할 수 있습니다. CWE-192는 이러한 강제적인 형 변환에 대한 위험을 말하고 있습니다.

위 예제는 소켓으로부터 입력된 패킷을 읽어 헤더 수를 확인하고 추출하는 코드입니다. 헤더의 값을 저장하는 변수 numHeaders는 signed int 타입으로 정의되어져 있기 때문에 음수가 저장될 수 있습니다. 만약 입력된 패킷 내 헤더의 값이 음수일 경우, malloc의 사이즈 인자 값은 음수가 되고 이는 malloc 함수의 사이즈 인자인 size_t(unsigned int) 타입으로 묵시적인 형 변환이 발생하게 됩니다. 이 형 변환으로 인해 음수는 매우 큰 값으로 변경되며 결국 malloc 함수 실행이 실패하거나 매우 큰 크기의 동적 메모리 할당이 이루어지게 됩니다. 이처럼 매우 크거나 매우 작은 값이 아닌, 즉 특별하지 않은 일반적인 음수만을 통해 메모리 할당 함수의 수행을 방해하여 버퍼 오버 플로우를 발생하는 공격이 발생하기도 합니다.
이 문제를 회피하기 위해서는 설계 및 디자인 단계에서 프로그램 흐름 상 불필요한 객체 간의 복잡한 형 변환을 피하도록 사전에 계획하거나 프로그램 실행 시, 사용되는 데이터 타입 과 형 변환 수행에 대한 정확한 확인을 사전에 수행하는 것이 중요합니다. 묵시적인 형 변환은 CodeSonar와 같은 자동화 정적 분석 도구로 쉽고 빠르게 검사가 가능하며, 이외 형 변환으로 인해 발생할 수 있는 오버 플로우와 같은 문제도 함께 검사하여 소스 코드를 통해 프로그램 실행 오류 또는 보안 문제를 사전에 예방하고 방지할 수 있습니다.
l CWE-194: Unexpected Sign Extension
CWE-194는 큰 데이터 타입으로 변환될 때 부호가 확장됨으로써 숫자가 변경되는 문제를 말합니다. 특히 본래 숫자가 음수일 경우 부호가 확장되면 의도하지 않는 값이 생성되고 이로 인해 SW 취약점이 발생할 수 있습니다. 예를 들어, 의도하지 않은 부호 확장이 사이즈 인자 값 또는 배열의 인덱스에서 일어날 경우 이는 프로그램에서 의도된 버퍼의 범위를 벗어나 데이터가 읽고 쓰여지는 문제가 발생하게 됩니다.
아래 예제 코드는 데이터 복사 전 사이즈 인자에 대하여 최대 크기 및 Sanity Check(새너티 검사, 연산의 결과가 참인지 평가하는 기본적인 테스트)를 수행한 뒤 strncpy() 함수를 통해 문자열 복사를 수행하고 있습니다. GetUntrustedInt() 함수로부터 리턴된 값은 변수 i에 저장된 후 short형 변수 s에 다시 저장됩니다. (이 예시에서는 short 형 변수가 사용되었지만 일반적으로 배열과 같은 구조화된 데이터를 처리하는 코드에서는 short 타입을 사용하지 않으며 주로int 타입이 사용됩니다.) 이후 변수 s를 통해 배열의 최대 크기를 초과하는지를 검사한 뒤, strncpy() 함수의 사이즈 인자로 사용되어 데이터 복사를 수행합니다. 그러나 s는 ‘-1’ 음수 값을 가지며 strncpy() 함수의 사이즈 인자로 참조되어 부호 확장 발생 후 size_t 타입 즉, unsigned int 가 가질 수 있는 가장 큰 값인 4294967295가 되어 버퍼 오버 플로우를 발생시킵니다.

이처럼 배열의 인덱스나 특정 객체의 사이즈를 표기하는 변수와 같이 음수 값을 표현할 필요가 없을 경우에는 가능한 Signed 변수 사용을 지양하는 것이 좋습니다. 만약 음수의 데이터를 사용할 경우에는 해당 값을 함수의 인자로 사용하거나 해당 값을 참조하기 전 저장된 값에 대한 새너티(Sanity) 검사를 수행하는 것이 좋습니다.
출처 : CWE 웹사이트 (http://cwe.mitre.org/index.html)
