3 Sep
2014
Posted in: 코드
By    2 Comments

MFC 위성 DLL 취약점


MFC 위성 DLL 취약점
by 신영진(YoungJin Shin), codewiz at gmail.com, @codemaru, http://www.jiniya.net

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을 사용하신다면 일이 조금 골치아파질수도 있겠네요.

mfc100

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

mfc71

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


  • 트랙백 주소: http://www.jiniya.net/wp/archives/14240/trackback

관련 글

  • 쟁이

    LoadLibrary 직전의 push eax는 DLL의 파일네임일테고, 그 위의 snprintf는 파일네임을 만들기 위한 호출이었을 듯… 결국 중간쯤에 있는 add esp, 10h로 봐서 보이는 소스 맨 위의 push esi나 그 전에 push했던 것을 바꾸어야 하지 않을까 싶네여…

    하지만 mfc100.dll의 수정방법도 미완성이군요.
    앞서의 VS 2008의 소스코드를 보면 LoadLibrary를 호출하는 곳이 2군데니까 수정도 2군데를 해야겠죠. 첫번째 인자만 다르니까 컴파일러가 최적화 과정에서 다른 곳에서 jmp 했을거라 생각할 수도 있지만 위의 어셈코드에서는 조건비교가 없는 것으로 보아서는 다른 곳에서 인자 새로 넣고 다시 호출했을 거 같네여.

    아닌가요? ㅎㅎㅎ제 실력은 여기까지라서 자신은 없네요.

  • YoungJin Shin

    LoadLibrary는 flag가 들어가는 곳이 없기 때문에 별도 코드를 추가해야 합니다. mfc100.dll의 수정법의 경우는 한 곳만 표기한 것이구요. 다른 곳도 찾아야겠지요. 코드는 컴파일러가 함수를 쪼개서 저렇게 나타난 것 같네요.