응용 프로그램 보안에 있어 중요한 일보 전진: C 런타임 및 Windows API에서의 보안 문제

응용 프로그램 보안에 있어 중요한 일보 전진: C 런타임 및 Windows API에서의 보안 문제

Michael Howard

보안 프로그램 관리자

Windows 보안 팀

Windows XP 팀

Microsoft Corporation

요약 : 이 기사에서는 C와 C++를 사용하여 함수를 호출할 때의 일반적인 실수와 이 때문에 발생할 수 있는 보안 문제에 대해 설명하고 몇가지 함수의 사용법에 대해 간단히 설명합니다. 이에 대한 문제는 지속적으로 다룰 것이며 앞으로 몇 달 간 여러 가지 API에 대한 보안 정보를 제공하도록 하겠습니다(6페이지/인쇄 페이지 기준).

소개

C와 C++의 코드를 검토하여 보안상 취약점을 찾는 과정에서 필자는 특정 함수 호출을 사용하는 방법에 있어서의 일반적인 오류를 발견하게 되었습니다. 함수 호출이 보안과 직접 관련되지는 않지만 함수 호출을 제대로 사용하지 않을 경우 복잡한 보안 문제가 발생할 수 있습니다.

이 기사에서는 이러한 오류와 오류로 인해 발생할 수 있는 보안 문제에 대해 설명하고 몇몇 함수의 사용법에 대해 간단히 설명합니다.

저희는 MSDN과 Platform SDK(진행 중)에 문서화된 몇몇 API와 Microsoft 제품과 Microsoft 제품이 아닌 다른 제품 모두에서 보안상의 취약점을 야기할 수 있는 최상위 함수 호출에 대해 간략히 설명한 첫 번째 논의를 시작으로 보안 문제에 대해 논의하기 시작했습니다.

무엇보다도 다음 함수에는 특별히 더 주목해야 할 보안상 문제가 있습니다.

CopyMemoryCopyMemory
CreateProcess, CreateProcessAsUser, CreateProcessWithLogonWCreateProcess, CreateProcessAsUser, CreateProcessWithLogonW
SetSecurityDescriptorDaclSetSecurityDescriptorDacl
가장 함수가장 함수
memcpymemcpy
sprintf, swprintfsprintf, swprintf
strcat, wcscat, _mbscatstrcat, wcscat, _mbscat
strcpy, wcscpy, _mbscpystrcpy, wcscpy, _mbscpy
strncat, wcsncat, _mbsncatstrncat, wcsncat, _mbsncat
WinExecWinExec

CopyMemory

보안 주의 사항

첫째 인수인 DestinationSource의 총 count 바이트를 보관할 수 있을 만큼 커야 합니다. 그렇지 않으면 버퍼 오버런이 발생합니다. 그러면 액세스 위반이 발생할 경우 응용 프로그램에 대한 서비스 공격을 거부하거나 최악의 경우 공격자가 프로세스에 실행 코드를 삽입할 수 있습니다. 특히 Destination이 스택 기반 버퍼일 경우에는 더 그렇습니다. 마지막 인수 LengthDestination의 크기가 아니라 Destination에 복사할 바이트 수임에 유의하십시오.

다음 코드 예제는 안전하게 CopyMemory()를 사용하는 방법을 보여 줍니다.

void test(char *pbData, unsigned int cbData) { char buf[BUFFER_SIZE];CopyMemory(buf, pbData, min(cbData,BUFFER_SIZE));}

CreateProcess, CreateProcessAsUser, CreateProcessWithLogonW

보안 주의 사항

첫째 매개 변수 lpApplicationName은 NULL이 될 수 있는데 이 경우 실행 파일의 이름이 lpCommandLine의 공백으로 구분된 첫 번째 문자열이 되어야 합니다. 그러나 실행 파일 이름이나 경로 이름에 공백이 있으면 공백을 적절히 처리하지 않을 경우 악의적인 실행 파일이 실행될 수 있습니다. 다음 예제는 "Program.exe"가 있을 경우 "foo.exe" 대신 "Program.exe"가 실행되기 때문에 위험합니다.

CreateProcess(NULL, "C:\Program Files\foo", ...)

이 경우 악의적인 사용자가 시스템에 "Program.exe"라는 트로이의 목마 프로그램을 만들면 Program Files 디렉터리를 사용하여 CreateProcess를 잘못 호출하는 모든 프로그램으로 인해 의도했던 응용 프로그램 대신 트로이의 목마 프로그램이 실행됩니다.

함수가 런타임 매개 변수에서 실행 파일의 경로 이름을 분석하고 결정하지 않도록 하려면 lpApplicationName에 NULL을 전달하지 않도록 하십시오. 또는 아래 예제에서와 같이 lpApplicationName이 NULL이면 lpCommandLine에서 실행 파일 경로를 따옴표로 묶으십시오.

CreateProcess(NULL, "\"C:\Program Files\foo.exe\" -L -S", ...)

SetSecurityDescriptorDacl

보안 주의 사항

가능하면 NULL DACL(즉, pDacl이 NULL)을 갖는 보안 설명자를 만들지 않아야 합니다. 이런 DACL은 개체에 보안을 전혀 제공하지 않습니다. 이 경우 공격자가 개체에 모든 사람(모든 액세스 거부) ACE를 설정하여 관리자를 비롯한 모든 사람이 개체를 액세스할 수 없게 만들 수 있습니다. 즉, NULL DACL은 개체를 공격으로부터 전혀 보호하지 않습니다.

가장 함수

보안 주의 사항

어떤 이유로든 가장 함수 호출이 실패하면 클라이언트를 가장하지 못하고 클라이언트 요청은 호출이 만들어진 프로세스의 보안 컨텍스트에서 만들어집니다. 프로세스가 LocalSystem이나 관리 그룹의 구성원과 같은 권한이 많은 계정으로 실행될 경우에는 사용자가 다른 계정으로 실행될 경우에는 거부되었을 작업을 수행할 수 있습니다. 따라서 항상 호출의 반환 값을 확인하고 호출이 실패하여 오류를 발생시킬 경우에는 클라이언트 요청의 실행을 중지해야 합니다. 이러한 호출에 대한 예제는 다음과 같습니다.

RpcImpersonateClient

ImpersonateNamedPipeClient

SetThreatToken

ImpersonateSelf

CoImpersonateClient

ImpersonateDdeClientWindow

ImpersonateSecurityContext

ImpersonateLoggedOnUser

memcpy

보안 주의 사항

첫째 인수인 destsrc의 총 count 바이트를 보관할 수 있을 만큼 커야 합니다. 그렇지 않으면 버퍼 오버런이 발생합니다. 그러면 액세스 위반이 발생할 경우 응용 프로그램에 대한 서비스 공격을 거부하거나 최악의 경우 공격자가 프로세스에 실행 코드를 삽입할 수 있습니다. dest가 스택 기반 버퍼일 경우 특히 그렇습니다. 마지막 인수 countdest의 크기가 아니라 dest에 복사할 바이트 수임에 유의하십시오.

다음 코드 예제는 안전하게 memcpy()를 사용하는 방법을 보여 줍니다.

void test(char *pbData, unsigned int cbData) { char buf[BUFFER_SIZE]; memcpy(buf, pbData, min(cbData,BUFFER_SIZE));}

sprintf, swprintf

보안 주의 사항

첫째 인수 bufferformat의 포맷된 버전과 뒤에 나오는 NULL('\0') 문자를 보관할 수 있을 만큼 커야 합니다. 그렇지 않으면 버퍼 오버런이 발생합니다. 그러면 액세스 위반이 발생할 경우 응용 프로그램에 대한 서비스 공격을 거부하거나 최악의 경우 공격자가 프로세스에 실행 코드를 삽입할 수 있습니다. buffer가 스택 기반 버퍼일 경우 특히 그렇습니다.

뿐만 아니라 사용자나 응용 프로그램에서 format을 변수로 제공할 수 있는 위험도 있다는 점에 유의하십시오. 다음 예제에서는 공격자가 szTemplate을 "%90s%10s"로 설정할 경우 100바이트 문자열이 만들어지기 때문에 위험합니다.

void test(char *szTemplate,char *szData1, char *szData2) { char buf[BUFFER_SIZE]; sprintf(buf,szTemplate,szData1,szData2);}

대신 _snprintf 나 _snwprintf를 사용하는 것을 고려해 보십시오.

strcat, wcscat, _mbscat

보안 주의 사항

첫째 인수 strDestination은 현재 strDestinationstrSource를 결합한 것과 닫는 '\0'을 보관할 만큼 커야 합니다. 그렇지 않으면 버퍼 오버런이 발생합니다. 그러면 액세스 위반이 발생할 경우 응용 프로그램에 대한 서비스 공격을 거부하거나 최악의 경우 공격자가 프로세스에 실행 코드를 삽입할 수 있습니다. strDestination이 스택 기반 버퍼일 경우 특히 그렇습니다. strncat, wcsncat 또는 _mbsncat을 사용하는 것을 고려해 보십시오.

strcpy, wcscpy, _mbscpy

보안 주의 사항

첫째 인수 strDestinationstrSource와 닫는 '\0'을 보관할 만큼 커야 합니다. 그렇지 않으면 버퍼 오버런이 발생합니다. 그러면 액세스 위반이 발생할 경우 응용 프로그램에 대한 서비스 공격을 거부하거나 최악의 경우 공격자가 프로세스에 실행 코드를 삽입할 수 있습니다. strDestination이 스택 기반 버퍼일 경우 특히 그렇습니다. strncpy, wcsncpy 또는 _mbsncpy를 사용하는 것을 고려해 보십시오.

strncat, wcsncat, _mbsncat

보안 주의 사항

첫째 인수 strDestination은 현재 strDestinationstrSource를 조합한 것과 닫는 NULL('\0')을 보관할 만큼 커야 합니다. 그렇지 않으면 버퍼 오버런이 발생합니다. 그러면 액세스 위반이 발생할 경우 응용 프로그램에 대한 서비스 공격을 거부하거나 최악의 경우 공격자가 프로세스에 실행 코드를 삽입할 수 있습니다. strDestination이 스택 기반 버퍼일 경우 특히 그렇습니다. 마지막 인수 countstrDestination의 크기가 아니라 strDestination에 복사할 바이트 수임에 유의하십시오.

뿐만 아니라 strncatstrDestination 버퍼에 남은 공간이 있을 경우에만 뒤에 나오는 NULL을 추가한다는 점에 유의하십시오.

다음 코드 예제는 안전하게 strncat을 사용하는 방법을 보여 줍니다

void test(char *szWords1, char *szWords2) { char buf[BUFFER_SIZE]; strncpy(buf,szWords1,sizeof buf - 1); buf[BUFFER_SIZE - 1] = '\0'; unsigned int cRemaining = (sizeof buf - strlen(buf)) - 1; strncat(buf,szWords2,cRemaining);}

WinExec

보안 주의 사항

실행 파일 이름은 lpCmdLine의 공백으로 구분된 첫 번째 문자열로 처리됩니다. 그러나 실행 파일 이름이나 경로 이름에 공백이 있으면 공백을 적절히 처리하지 않을 경우 악의적인 실행 파일이 실행될 수 있습니다. 다음 예제는 "Program.exe"가 있을 경우 "foo.exe" 대신 "Program.exe"가 실행되기 때문에 위험합니다.

WinExec("C:\Program Files\foo", ...)

이 경우 악의적인 사용자가 시스템에 "Program.exe"라는 트로이의 목마 프로그램을 만들면 Program Files 디렉터리를 사용하여 WinExec를 잘못 호출하는 모든 프로그램으로 인해 의도했던 응용 프로그램 대신 트로이의 목마 프로그램이 실행됩니다.

보안을 위해서는 WinExec 대신 CreateProcess를 사용하는 것이 훨씬 좋습니다. 그러나 이전 버전과의 호환을 위해 WinExec를 사용해야 할 경우에는 다음 예제에서와 같이 응용 프로그램 이름을 따옴표로 묶도록 하십시오.

WinExec("\"C:\Program Files\foo.exe\" -L -S", ...)

결론

앞으로도 저희는 잘못 사용할 수 있는 이러한 함수에 대해 보다 많은 정보를 얻게 되면 이에 대한 설명을 제공할 것입니다. 이러한 함수 호출에 대한 의견이 있으시면 언제든지 mikehow@microsoft.comtoUS.gif으로 전자 메일을 보내 주십시오.


http://msdn.microsoft.com/ko-kr/library/cc671586.aspx

by o사랑o | 2008/10/17 12:59 | 보안/디버깅 | 트랙백 | 덧글(0)

◀ 이전 페이지다음 페이지 ▶