#security #winapi
#Безопасность #winapi
Вопрос:
Итак, я пытаюсь выяснить, могу ли я получить повышенные привилегии для текущего процесса, дублируя токен из процесса с повышенными правами.
1.In процесс 1 Я вызываю процесс 2 с помощью администратора, передавая PID процесса 1:
RunAsAdmin(L"test.exe /admin pid"); // Calls ShellExecute with runas and waits to finish
2.In процесс с повышенными правами Я дублирую токен с повышенными правами (удалена проверка ошибок):
HANDLE h3 = 0;
OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, amp;h3);
// Verification if token is elevated, yes it is
TOKEN_ELEVATION te;
TOKEN_ELEVATION_TYPE tet;
DWORD l;
GetTokenInformation(h3, TOKEN_INFORMATION_CLASS::TokenElevation, amp;te, sizeof(te), amp;l);
GetTokenInformation(h3, TOKEN_INFORMATION_CLASS::TokenElevationType, amp;tet, sizeof(tet), amp;l);
// Duplicate the token to the process which we have the PID from cmdline:
auto hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
HANDLE nh = 0;
DuplicateHandle(GetCurrentProcess(), h3, hProcess, amp;nh, 0, 0,DUPLICATE_SAME_ACCESS);
// Verification if new handle is elevated, yes it is
// Pass token to process
PassNewTokenToProcess(nh); // Currently I'm using clipboard, later I will use file mapping.
3.In процесс 1 Я получаю токен:
HANDLE hTok = GetTokenFromClipboard();
// Verify it's elevated, yes it is.
// Duplicate it to myself
HANDLE hTok2;
DuplicateTokenEx(hTok, TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, amp;hTok2);
// Verify it's elevated, yes it is.
// Impersonate
ImpersonateLoggedOnUser(hTok2); // Returns true
RegCreateKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\TestXXX", 0, 0, 0, KEY_ALL_ACCESS, ...);
// Fails error 5 access denied
Последний вызов завершается с ошибкой. Я знаю, что делаю что-то неправильно, но токены повышаются как GetTokenInformation
возвраты.
Могу ли я где-нибудь убедиться, что новый токен действительно не сможет повысить уровень текущего процесса?
Большое спасибо.
Комментарии:
1. Разве не известно, что вы не можете этого сделать?
2. @DavidHeffernan Я ищу способ узнать, что я не могу этого сделать, т. Е. Какая-то ошибка, возвращаемая функциями, которые я вызываю.
3. Ожидается, что вы создадите новый процесс с повышенными правами. Почему вы не хотите этого делать?
4. Вы уже запускаете процесс с повышенными правами, поэтому просто заставьте этот процесс выполнять работу, для которой вам нужны повышенные права. Нет смысла пытаться передать повышенный токен обратно не повышенному процессу, чтобы он мог выдавать себя за другой.
5. @David: Несмотря на рекомендации, при создании нового процесса отсутствует важная функция: возможность восстановления маркера безопасности процесса. Это обычный сценарий, когда повышение уровня требуется только для короткой последовательности операций. Повышение уровня одного потока было бы хорошим решением, позволяющим либо восстановить его токен, либо позволить ему работать до завершения. Я всегда задавался вопросом, почему в API нет ничего, что учитывало бы этот распространенный сценарий.
Ответ №1:
проблема здесь в вызове
// Impersonate
ImpersonateLoggedOnUser(hTok2); // Returns true
несмотря на ImpersonateLoggedOnUser
возврат true, на самом деле функция может делать не то, чего вы ждете.
Все функции олицетворения, включая ImpersonateLoggedOnUser, разрешают запрошенное олицетворение, если одно из следующих значений верно:
- Запрошенный уровень олицетворения токена ниже, чем уровень безопасности, такой как SecurityIdentification или SecurityAnonymous.
- Вызывающий имеет привилегию SeImpersonatePrivilege.
…
таким образом, даже для действительного api hToken может произойти сбой, но необычным способом — вместо возврата false и установки кода ошибки — api в любом случае возвращает true, но автоматически сбрасывает уровень олицетворения токена потока на SecurityIdentification
. после этого все вызовы, которые выполняют проверки безопасности, завершаются неудачей с помощью ERROR_BAD_IMPERSONATION_LEVEL
или ERROR_ACCESS_DENIED
.
в общем случае процесс без повышенных прав не имеет SeImpersonatePrivilege
привилегий, в результате, когда мы пытаемся выдать себя за токен с повышенными правами, вызов формально не завершается неудачей, но вместо этого мы получаем SecurityIdentification
уровень олицетворения SecurityImpersonation
. даже при повышении уровня (или в локальной системе, или даже в режиме ядра) попробуйте установить повышенный токен для потока в процессе, который не имеет SeImpersonatePrivilege
привилегий — это попытка установить только SecurityIdentification
уровень.
мы можем легко проверить это, OpenThreadToken
вызвав и запросив его TokenImpersonationLevel
.
полный тестовый код:
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
void WINAPI entry(void*)
{
HANDLE hToken, hThread, hDupToken;
ULONG dwError;
// for attach debugger
MessageBoxW(0, 0, GetCommandLineW(), 0);
if (PWSTR c = wcschr(GetCommandLineW(), '*'))
{
ULONG dwThreadId = wcstoul(c 1, amp;c, 16);
dwError = ERROR_INVALID_PARAMETER;
if (!*c amp;amp; dwThreadId)
{
dwError = BOOL_TO_ERROR(OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE|TOKEN_IMPERSONATE, amp;hToken));
if (dwError == NOERROR)
{
dwError = BOOL_TO_ERROR(DuplicateToken(hToken, ::SecurityImpersonation, amp;hDupToken));
CloseHandle(hToken);
if (dwError == NOERROR)
{
if (hThread = OpenThread(THREAD_SET_THREAD_TOKEN, FALSE, dwThreadId))
{
dwError = RtlNtStatusToDosError(
ZwSetInformationThread(hThread, ThreadImpersonationToken, amp;hDupToken, sizeof(hDupToken))
);
CloseHandle(hThread);
}
else
{
dwError = GetLastError();
}
CloseHandle(hDupToken);
}
}
}
ExitProcess(dwError);
}
WCHAR file[MAX_PATH], params[32];
if (GetModuleFileNameW(0, file, RTL_NUMBER_OF(file)))
{
SHELLEXECUTEINFOW sei = {
sizeof(sei), SEE_MASK_NOCLOSEPROCESS, 0, L"runas", file, params
};
swprintf(params, L"*%x", GetCurrentThreadId());
dwError = BOOL_TO_ERROR(ShellExecuteExW(amp;sei));
if (dwError == NOERROR)
{
switch (WaitForSingleObject(sei.hProcess, INFINITE))
{
case WAIT_OBJECT_0:
if (GetExitCodeProcess(sei.hProcess, amp;dwError))
{
break;
}
case WAIT_FAILED:
dwError = GetLastError();
break;
default:
__debugbreak();
dwError = ERROR_GEN_FAILURE;
}
CloseHandle(sei.hProcess);
if (dwError == NOERROR)
{
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, amp;hToken))
{
ULONG cb;
SECURITY_IMPERSONATION_LEVEL sil;
if (GetTokenInformation(hToken, ::TokenImpersonationLevel, amp;sil, sizeof(sil), amp;cb))
{
DbgPrint("ImpersonationLevel = %xn", sil);
}
CloseHandle(hThread);
}
union {
HANDLE hFile;
HKEY hKey;
};
hFile = CreateFileW(file, 0, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
{
DbgPrint("CreateFile=%un", GetLastError());
}
else
{
CloseHandle(hFile);
}
dwError = RegOpenKeyExW(HKEY_CURRENT_USER, 0, 0, KEY_READ, amp;hKey);
if (dwError)
{
DbgPrint("OpenKey=%un", dwError);
}
else
{
RegCloseKey(hKey);
}
}
}
}
ExitProcess(dwError);
}
так что это задумано — отключите процессы с не повышенным (ниже высокого уровня целостности) уровнем, олицетворяющие повышенные токены, путем удаления SeImpersonatePrivilege
из токена процесса. это эффективно устраняет все попытки олицетворения для установки высокого или более уровня целостности