MFC 위성 DLL 취약점

@codemaru · September 03, 2014 · 5 min read

MFC 7.0부터 위성 DLL이란게 추가된 것 같습니다. 이게 머 위성 DLL하니 말이 거창한데 리소스 DLL을 분리시켜 놓고 시스템 언어에 맞게 리소스 DLL을 로드하는 것을 의미하나 봅니다. 상세 기능은 여기 링크를 참고하시면 됩니다. 근데 안타깝게도 MFC 개발팀에서 이 기능을 만들면서 초기에 소소한 실수(?!)를 하는 바람에 취약점이 생겼나 보네요. 머 대단한 취약점은 아니고 LPK.DLL이나 USP10.DLL 같이 임의의 코드가 리소스 DLL에 감염돼 있으면 실행되는 취약점입니다. 그럼 왜 생겼는지 한 번 살펴볼까요?

#0

아래는 VS 2008의 위성 DLL 로드 부분 코드입니다. MFC에 포함된 appcore.cpp의 _AfxLoadLangDLL 코드의 일부입니다. 보면 리소스 DLL을 LoadLibraryEx를 통해서 로드하는데 안타깝게도 마지막 인자가 0입니다. 0이라는 말은 그냥 일반 DLL 처럼 로드하겠다는 의미죠. 리소스 DLL에 악성 코드가 감염되어 있었다면 같이 실행될 수 있다는 것을 의미합니다.

if (pfnFindActCtxSectionString &&
	pfnFindActCtxSectionString(0, NULL, ...))
{
	// Load using the dll name only...
	hInstance = ::LoadLibraryEx(pszFilename, NULL, 0);
}
else
{
	// Load using the full path...
	hInstance = ::LoadLibraryEx(szLangDLL, NULL, 0);
}

취약한 VS 2008 위성 DLL 로드 부분 아래는 VS 2013의 appcore.cpp의 _AfxLoadLangDLL 함수 부분을 발췌한 내용입니다. 살펴보면 LoadLibraryEx를 할 때 마지막 인자에 LOAD_IMAGE_AS_DATAFILE을 넣어서 DllMain이 실행되지 않도록 하고 있습니다. 악성 코드에 감염되어 있다고 하더라도 해당 코드가 실행되지는 않는다는 것을 의미하죠.

hInstance = ::LoadLibraryExW(szLangDLL
           , NULL
           , LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);

if (hInstance == NULL)
{
	// if library load failed using flags only valid on Vista+, ...
	hInstance = ::LoadLibraryExW(szLangDLL, NULL, LOAD_LIBRARY_AS_DATAFILE);
}

안전한 VS 2013 위성 DLL 로드 부분 #1

이런 사실을 알게 된다면 굳이 위성 DLL 기능이 필요 없다면 기능을 사용하고 싶지 않을 수도 있는데요. 위성 DLL은 MFC DLL의 DllMain에서 아래와 같은 코드에 의해서 로드됩니다. 보시면 알겠지만 일반적인 상황이라면 제어할 수 있는 제어 플래그가 없습니다. 그도 그럴것이 뭐 DllMain이니깐염.

DWORD dwLen = GetModuleFileName(NULL, rgchFullModulePath, ...);
if (dwLen > 0)
{
	TCHAR *pszFilename = ::PathFindFileName(rgchFullModulePath);
	if (pszFilename > rgchFullModulePath)  // filename portion of path ...
	{
		*pszFilename = 0;
		// the buffer passed to AfxLoadLangResourceDLL is filled with the path ...
		g_fLoadingResourcesForMFCDLL = TRUE;
		hLangDLL = AfxLoadLangResourceDLL(_T("%s") _T("MFC") ...);
		g_fLoadingResourcesForMFCDLL = FALSE;
	}
}

AFX_MODULE_STATE* pState = AfxGetModuleState();
pState->m_appLangDLL = hLangDLL;

VS 2013 MFC DLL DllMain 부분 #2

이 MFC 취약점에 얽혀 있다면 빠져 나가는 방법으로는 1) VS를 업그레이드 한다. 2) MFC DLL을 새로 빌드해서 사용한다. 3) MFC DLL 바이너리를 패치 한다, 정도가 있겠습니다. 바이너리를 패치하는 게 가장 쉬워 보일 수도 있습니다. 플래그 값만 교정해주면 되니까요. 아래처럼 리소스 DLL을 LoadLibraryEx 해서 로드하는 부분을 찾아서 0을 2로 변경해 주면 됩니다. mfc71.dll을 사용하신다면 일이 조금 골치아파질수도 있겠네요.

MFC    DLL     md 0
mfc100.dll을 사용하신다면 LoadLibraryEx에 사용되는 마지막 플래그를 0에서 2로 변경해 주시면 됩니다.

MFC    DLL     md 1
mfc71.dll을 사용하시나요? 일단 눈물 좀 닦고. 상상력을 발휘해 보세요.

@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중