CreateProcess 系で作成元ログオンSIDから終了できないようにする

Windows の API でプロセスを作成する CreateProcess や CreateProcessWithLogonW などで作成した時に
Vista 以降では一般ユーザーから管理者ユーザなどの上位権限で稼動しているプロセスを終了できてしまうので回避方法のメモ

ソースとプロジェクトファイル一式 CreateProcessTest20130713.zip

プロセス作成時のプロセスとスレッドに対する SECURITY_ATTRIBUTES を設定しなかった場合に
Vista 以降はログオン SIDの ACL が DACL に追加されるようになっているようです。

プロセスをサスペンド状態(CREATE_SUSPENDED)で作成して
DACL からログオンセッションの ACL を削除して ResumeThread することで同一ログオン SID のプロセスからでも終了できなくなります。

なお呼び出し元から PROCESS_INFORMATION.hProcess を使用して終了させることはできます。

手順としては、

現在のログオン SID(S-1-5-5-aaa-bbb) が必要
参照 URL: http://msdn.microsoft.com/en-us/library/windows/desktop/aa446670%28v=vs.85%29.aspx

実際に作成した子プロセスのプロセスハンドルとスレッドハンドルの DACL を取得して
ログオン SID の ACCESS_ALLOWED_ACE を削除して上書き設定

でログオン SID の許可を削除できます。

ソースコードはこんな感じになっています。

/**
 * The MIT License
 * 
 * Copyright (C) 2013 KK.Kon
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

//#include "stdafx.h"

#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0502
#endif
#include <windows.h>

#include <tchar.h>

#if 1
#define MyDbgOutput() \
	{ \
		const	DWORD	dwErr = ::GetLastError(); \
		wchar_t	temp[1024]; \
		::wsprintfW( temp, L"%s(%u): 0x%08X\n", __FILE__, __LINE__, dwErr ); \
		::OutputDebugStringW( temp ); \
	}
#else
#define MyDbgOutput()
#endif





// LogonSID http://msdn.microsoft.com/en-us/library/windows/desktop/aa446670%28v=vs.85%29.aspx

bool
allocLogonSID( PSID *ppSID )
{
	bool result = false;

	HANDLE hToken = NULL;
	{
		const BOOL BRet = ::OpenProcessToken( ::GetCurrentProcess(), TOKEN_QUERY, &hToken );
		if ( FALSE == BRet )
		{
			MyDbgOutput();

			return false;
		}
	}

	*ppSID = NULL;

	const HANDLE	hHeap = ::GetProcessHeap();;

	if ( NULL != hToken )
	{
		TOKEN_GROUPS *	pTokenGroups = NULL;
		DWORD	nLength = 0;
		DWORD	nLengthNeeded = 0;

		::GetTokenInformation( hToken, TokenGroups, pTokenGroups, nLength, &nLengthNeeded );
		{
			pTokenGroups = reinterpret_cast<TOKEN_GROUPS *>( ::HeapAlloc( hHeap, HEAP_ZERO_MEMORY, nLengthNeeded+16 ) );
			nLength = nLengthNeeded;
			nLengthNeeded = 0;
			const BOOL BRet = ::GetTokenInformation( hToken, TokenGroups, pTokenGroups, nLength, &nLengthNeeded );
			if ( FALSE == BRet )
			{
				MyDbgOutput();
			}

			if ( NULL != pTokenGroups )
			{
				for ( size_t index = 0; index < pTokenGroups->GroupCount; ++index )
				{
					if ( SE_GROUP_LOGON_ID == (SE_GROUP_LOGON_ID & pTokenGroups->Groups[index].Attributes) )
					{
						PSID pSIDSrc = pTokenGroups->Groups[index].Sid;

						const size_t nLengthSID = ::GetLengthSid( pSIDSrc );
						PSID pSIDDest = reinterpret_cast<PSID>( ::HeapAlloc( hHeap, HEAP_ZERO_MEMORY, (nLengthSID+16) ) );
						if ( NULL == pSIDDest )
						{
							MyDbgOutput();
						}
						else
						{
							const BOOL BRet = ::CopySid( nLength, pSIDDest, pSIDSrc );
							if ( FALSE == BRet )
							{
								MyDbgOutput();

								::HeapFree( hHeap, 0, pSIDDest );
								pSIDDest = NULL;
							}
							else
							{
								*ppSID = pSIDDest;
								result = true;
							}
						}
						break;
					}
				}
			}

			if ( NULL != pTokenGroups )
			{
				::HeapFree( hHeap, 0, pTokenGroups );
				pTokenGroups = NULL;
			}
		}
	}

	if ( NULL != hToken )
	{
		::CloseHandle( hToken );
	}

	return result;
}

bool
freeLogonSID( PSID * ppSID )
{
	if ( NULL == ppSID )
	{
		return false;
	}

	bool result = false;

	if ( NULL != ppSID )
	{
		const HANDLE hHeap = ::GetProcessHeap();

		const BOOL BRet = ::HeapFree( hHeap, 0, *ppSID );
		if ( BRet )
		{
			*ppSID = NULL;
			result = true;
		}
	}

	return result;
}

#include <sddl.h> // ConvertSidToStringSid
void
dumpAccessAllowedACE( ACCESS_ALLOWED_ACE * pAce )
{
	{
		wchar_t	temp[2048];
		::wsprintfW( temp, L"ACCESS_ALLOWED_ACE %p\n", pAce );
		::OutputDebugStringW( temp );
	}

	wchar_t	szName[256];
	DWORD cchName = sizeof(szName)/sizeof(szName[0]);
	wchar_t	szDomainName[256];
	DWORD cchDomainName = sizeof(szDomainName)/sizeof(szName[0]);
	SID_NAME_USE	snu;

	szName[0] = '\0';
	szDomainName[0] = '\0';

	const BOOL BRet = ::LookupAccountSidW(
		NULL
		, &(pAce->SidStart)
		, szName
		, &cchName
		, szDomainName
		, &cchDomainName
		, &snu
		);

	{
		wchar_t	temp[2048];
		::wsprintfW( temp, L" Mask: 0x%08X\n", pAce->Mask );
		::OutputDebugStringW( temp );
	}

	if ( BRet )
	{
		wchar_t	temp[2048];
		::wsprintfW( temp, L" Name:%s DomainName:%s\n", szName, szDomainName );
		::OutputDebugStringW( temp );
	}

	{
		LPWSTR pStrSID = NULL;
		::ConvertSidToStringSidW( &(pAce->SidStart), &pStrSID );

		wchar_t	temp[2048];
		::wsprintfW( temp, L" SID: %s\n", pStrSID );
		::OutputDebugStringW( temp );

		::LocalFree( reinterpret_cast<HLOCAL>(pStrSID) );
	}
}

bool
handleDetachFromLogonSID( HANDLE hHandle )
{
	bool result = false;

	__try
	{
		const HANDLE	hHeap = ::GetProcessHeap();;

		PSID	pLogonSID = NULL;
		allocLogonSID( &pLogonSID );

		{
			SECURITY_INFORMATION	securityInfo = DACL_SECURITY_INFORMATION;
			SECURITY_DESCRIPTOR *	pSD = NULL;
			DWORD nLength = 0;
			DWORD nLengthNeeded = 0;
			::GetUserObjectSecurity( hHandle, &securityInfo, pSD, nLength, &nLengthNeeded );
			pSD = reinterpret_cast<SECURITY_DESCRIPTOR *>(::HeapAlloc( hHeap, HEAP_ZERO_MEMORY, (nLengthNeeded+16) ));
			nLength = nLengthNeeded;
			nLengthNeeded = 0;
			{
				const BOOL BRet = ::GetUserObjectSecurity( hHandle, &securityInfo, pSD, nLength, &nLengthNeeded );
				if ( FALSE == BRet )
				{
					MyDbgOutput();
				}
			}

			if ( NULL != pSD )
			{
				bool needContinue = true;

				while( needContinue )
				{
					BOOL	isPresent = FALSE;
					ACL *	pACL = NULL;
					BOOL	isDefault = FALSE;
					const BOOL BRet = ::GetSecurityDescriptorDacl( pSD, &isPresent, &pACL, &isDefault );
					if ( FALSE == BRet )
					{
						MyDbgOutput();

						needContinue = false;
					}

					if ( NULL != pACL )
					{
						DWORD index = 0;
						const DWORD count = pACL->AceCount;
						for ( index = 0; index < count; index++ )
						{
							bool needBreak = false;

							LPVOID	pRaw = NULL;
							const BOOL BRet = ::GetAce( pACL, index, &pRaw );
							if ( FALSE == BRet )
							{
								MyDbgOutput();
							}
							else
							if ( NULL != pRaw )
							{
								ACE_HEADER *	pHeader = reinterpret_cast<ACE_HEADER *>(pRaw);
								switch( pHeader->AceType )
								{
								case ACCESS_ALLOWED_ACE_TYPE:
									{
										ACCESS_ALLOWED_ACE *	pAce = reinterpret_cast<ACCESS_ALLOWED_ACE *>(pRaw);

										dumpAccessAllowedACE( pAce );

										if ( ::EqualSid( &(pAce->SidStart), pLogonSID ) )
										{
											const BOOL BRet = ::DeleteAce( pACL, index );
											if ( FALSE == BRet )
											{
												MyDbgOutput();
											}
											else
											{
												needBreak = true;
											}
										}
									}
									break;
								}
							}

							if ( needBreak )
							{
								break;
							}
						} // for

						if ( count <= index )
						{
							needContinue = false;
						}
					}
				} // while ( needContinue )
			}

			if ( NULL != pSD )
			{
				const BOOL BRet = ::SetUserObjectSecurity( hHandle, &securityInfo, pSD );
				if ( FALSE == BRet )
				{
					MyDbgOutput();
				}
				else
				{
					result = true;
				}
			}

			::HeapFree( hHeap, 0, pSD );
		}

		freeLogonSID( &pLogonSID );

	}
	__except ( EXCEPTION_CONTINUE_EXECUTION )
	{
	}

	return result;
}

bool
test( LPCWSTR pUsername, LPCWSTR pDomain, LPCWSTR pPassword, const BOOL detachFromLogonSession )
{
	const DWORD	dwLogonFlags = LOGON_WITH_PROFILE;
	LPCWSTR		pApplicationName = L"C:\\Windows\\System32\\cmd.exe";
	LPWSTR		pCommandLine = NULL;
	const DWORD	dwCreationFlags = CREATE_NEW_PROCESS_GROUP|CREATE_SUSPENDED; //DETACHED_PROCESS;
	LPVOID		pEnvironment = NULL;
	LPCWSTR		pCurrentDirectory = NULL;
	STARTUPINFOW		startupInfo;
	PROCESS_INFORMATION	processInfo;
	ZeroMemory( &startupInfo, sizeof(startupInfo) );
	ZeroMemory( &processInfo, sizeof(processInfo) );
	wchar_t		currentDir[1024];

	::GetCurrentDirectoryW( sizeof(currentDir), currentDir );
	pCurrentDirectory = currentDir;

	const HANDLE	hHeap = ::GetProcessHeap();;

	const BOOL BRet = ::CreateProcessWithLogonW(
		pUsername
		, pDomain
		, pPassword
		, dwLogonFlags
		, pApplicationName
		, pCommandLine
		, dwCreationFlags
		, pEnvironment
		, pCurrentDirectory
		, &startupInfo
		, &processInfo
		);
	if ( FALSE == BRet )
	{
		MyDbgOutput();
	}
	else
	{
		if ( detachFromLogonSession )
		{
			handleDetachFromLogonSID( processInfo.hProcess );
			handleDetachFromLogonSID( processInfo.hThread );
		}

		::ResumeThread( processInfo.hThread );

#if 0
		{
			const BOOL BRet = ::TerminateProcess( processInfo.hProcess, 0 );
			if ( FALSE == BRet )
			{
				MyDbgOutput();
			}
		}
#endif

		::CloseHandle( processInfo.hProcess );
		::CloseHandle( processInfo.hThread );
	}

	return false;
}

bool
test2( const BOOL detachFromLogonSession )
{
	const DWORD	dwLogonFlags = LOGON_WITH_PROFILE;
	LPCWSTR		pApplicationName = L"C:\\Windows\\System32\\cmd.exe";
	LPWSTR		pCommandLine = NULL;
	LPSECURITY_ATTRIBUTES	pProcessAttributes = NULL;
	LPSECURITY_ATTRIBUTES	pThreadAttributes = NULL;
	BOOL bInheritHandles = FALSE;
	const DWORD	dwCreationFlags = CREATE_NEW_PROCESS_GROUP|CREATE_SUSPENDED; //DETACHED_PROCESS;
	LPVOID		pEnvironment = NULL;
	LPCWSTR		pCurrentDirectory = NULL;
	STARTUPINFOW		startupInfo;
	PROCESS_INFORMATION	processInfo;
	ZeroMemory( &startupInfo, sizeof(startupInfo) );
	ZeroMemory( &processInfo, sizeof(processInfo) );
	wchar_t		currentDir[1024];

	::GetCurrentDirectoryW( sizeof(currentDir), currentDir );
	pCurrentDirectory = currentDir;

	const HANDLE	hHeap = ::GetProcessHeap();;

	const BOOL BRet = ::CreateProcessW(
		pApplicationName
		, pCommandLine
		, pProcessAttributes
		, pThreadAttributes
		, bInheritHandles
		, dwCreationFlags
		, pEnvironment
		, pCurrentDirectory
		, &startupInfo
		, &processInfo
		);
	if ( FALSE == BRet )
	{
		MyDbgOutput();
	}
	else
	{
		if ( detachFromLogonSession )
		{
			handleDetachFromLogonSID( processInfo.hProcess );
			handleDetachFromLogonSID( processInfo.hThread );
		}
		::ResumeThread( processInfo.hThread );

#if 0
		{
			const BOOL BRet = ::TerminateProcess( processInfo.hProcess, 0 );
			if ( FALSE == BRet )
			{
				MyDbgOutput();
			}
		}
#endif

		::CloseHandle( processInfo.hProcess );
		::CloseHandle( processInfo.hThread );
	}

	return false;
}

int _tmain(int argc, _TCHAR* argv[])
{
	if ( argc <= 1)
	{
		return 1;
	}

	const wchar_t szUsernameUp[] = NULL; //L"";
	const wchar_t szDomainnameUp[] = NULL; //L"";
	const wchar_t szPasswordUp[] = NULL; //L"";

	const wchar_t szUsernameDown[] = NULL; //L"";
	const wchar_t szDomainnameDown[] = NULL; //L"";
	const wchar_t szPasswordDown[] = NULL; //L"";

	if ( NULL != argv[1] )
	{
		if ( 0 == _tcsicmp( argv[1], _T("up") ) )
		{
			const wchar_t * szUsername = szUsernameUp;
			const wchar_t * szDomainname = szDomainnameUp;
			const wchar_t * szPassword = szPasswordUp;
			test( szUsername, szDomainname, szPassword, false );
		}
		else
		if ( 0 == _tcsicmp( argv[1], _T("updetach") ) )
		{
			const wchar_t * szUsername = szUsernameUp;
			const wchar_t * szDomainname = szDomainnameUp;
			const wchar_t * szPassword = szPasswordUp;
			test( szUsername, szDomainname, szPassword, true );
		}
		else
		if ( 0 == _tcsicmp( argv[1], _T("down") ) )
		{
			const wchar_t * szUsername = szUsernameDown;
			const wchar_t * szDomainname = szDomainnameDown;
			const wchar_t * szPassword = szPasswordDown;
			test( szUsername, szDomainname, szPassword, false );
		}
		else
		if ( 0 == _tcsicmp( argv[1], _T("downdetach") ) )
		{
			const wchar_t * szUsername = szUsernameDown;
			const wchar_t * szDomainname = szDomainnameDown;
			const wchar_t * szPassword = szPasswordDown;
			test( szUsername, szDomainname, szPassword, true );
		}
		else
		if ( 0 == _tcsicmp( argv[1], _T("detach") ) )
		{
			test2( true );
		}

	}

	return 0;
}

This entry was posted in Windows, Windows API. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>