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.dll을 사용하신다면 LoadLibraryEx에 사용되는 마지막 플래그를 0에서 2로 변경해 주시면 됩니다.