Кто знает, как запустить процесс от имени другого пользователя? А если это Windows 7 с включенным UAC, а ваш пользователь имеет административные привилегии, потерять которые в запущенном процессе вы не хотите? Итак, дано: Windows 7, UAC, логин и пароль учётной записи с административными привилегиями, WinAPI и полная свобода действий. Требуется: программным путём запустить процесс от имени этого пользователя. Давайте разбираться.
Акт первый - интерактивный
Сначала вспомним, как работает UAC. При интерактивном входе с использованием административной учётной записи создаётся два токена: обычный и ограниченный. Ограниченный токен возвращается в качестве результата функции LogonUser, он же, в частности, используется системой для запуска приложений в интерактивной сессии по умолчанию. При запросе пользователя на запуск приложения в elevated-режиме, система извлекает основной токен и использует его для создания процесса. Наибольший интерес в данном случае представляет то, откуда именно извлекается основной токен. Это так называемый linked (связанный) токен, доступ к которому можно получить с помощью функции GetTokenInformation следующим образом:
Казалось бы вот оно счастье. Но любые попытки создать процесс, используя данный токен скорее всего будут завершаться с ошибкой ERROR_BAD_TOKEN_TYPE. Дело в том, что в обычной ситуации функция GetTokenInformation возвращает так называемый identification-токен (попробуйте запросить TokenImpersonationLevel этого токена с помощью той же функции GetTokenInformation). Всё, что вы можете сделать с этим токеном - это узнать его SID, прочитать в какие группы он входит и какими привилегиями обладает. Ни имперсонировать его, ни запустить с его помощью процесс, ни даже конвертировать его в основной (primary) токен с помощью функции DuplicateToken вы не сможете.
И только если у вас есть привилегия SeTcbPrivilege, linked-токен, возвращённый функцией GetTokenInfromation, оказывается основным токеном. Вот только по умолчанию этой привилегией обладает единственный пользователь в системе - Local System. И даже если вы выдадите её своему пользователю, вопреки всем рекомендациям, советующим не делать этого ни при каких условиях, она точно будет ограничена UAC`ом при интерактивном входе в систему. Единственное место, где вы имеете шанс воспользоваться подобной возможностью - это сервис, работающий под учётной записью Local System. Надо отдать должное UAC`у - даже там вам могут пригодиться подобные приседания.
Акт первый - интерактивный
Сначала вспомним, как работает UAC. При интерактивном входе с использованием административной учётной записи создаётся два токена: обычный и ограниченный. Ограниченный токен возвращается в качестве результата функции LogonUser, он же, в частности, используется системой для запуска приложений в интерактивной сессии по умолчанию. При запросе пользователя на запуск приложения в elevated-режиме, система извлекает основной токен и использует его для создания процесса. Наибольший интерес в данном случае представляет то, откуда именно извлекается основной токен. Это так называемый linked (связанный) токен, доступ к которому можно получить с помощью функции GetTokenInformation следующим образом:
HANDLE getLinkedToken(HANDLE token)
{
TOKEN_LINKED_TOKEN linkedToken;
DWORD retSize = 0;
if (!::GetTokenInformation(token, TokenLinkedToken,
&linkedToken, sizeof(linkedToken), &retSize)) {
DWORD error = ::GetLastError();
if (ERROR_NO_SUCH_LOGON_SESSION != error) {
// there are no linked token: UAC disabled,
// or standard user account is used, or ...
return 0;
} else if (ERROR_INVALID_PARAMETER != error) {
// linked tokens are not supported: program is executed
// on an old operation system
return 0;
} else {
// report error
throw std::runtime_error(formatWinError(error));
}
}
if (0 == linkedToken.LinkedToken
|| INVALID_HANDLE_VALUE == linkedToken.LinkedToken) {
// report unexpected error: linkedToken.LinkedToken
// contains an invalid token
throw std::runtime_error("Linked token is invalid");
}
return linkedToken.LinkedToken
}
{
TOKEN_LINKED_TOKEN linkedToken;
DWORD retSize = 0;
if (!::GetTokenInformation(token, TokenLinkedToken,
&linkedToken, sizeof(linkedToken), &retSize)) {
DWORD error = ::GetLastError();
if (ERROR_NO_SUCH_LOGON_SESSION != error) {
// there are no linked token: UAC disabled,
// or standard user account is used, or ...
return 0;
} else if (ERROR_INVALID_PARAMETER != error) {
// linked tokens are not supported: program is executed
// on an old operation system
return 0;
} else {
// report error
throw std::runtime_error(formatWinError(error));
}
}
if (0 == linkedToken.LinkedToken
|| INVALID_HANDLE_VALUE == linkedToken.LinkedToken) {
// report unexpected error: linkedToken.LinkedToken
// contains an invalid token
throw std::runtime_error("Linked token is invalid");
}
return linkedToken.LinkedToken
}
Казалось бы вот оно счастье. Но любые попытки создать процесс, используя данный токен скорее всего будут завершаться с ошибкой ERROR_BAD_TOKEN_TYPE. Дело в том, что в обычной ситуации функция GetTokenInformation возвращает так называемый identification-токен (попробуйте запросить TokenImpersonationLevel этого токена с помощью той же функции GetTokenInformation). Всё, что вы можете сделать с этим токеном - это узнать его SID, прочитать в какие группы он входит и какими привилегиями обладает. Ни имперсонировать его, ни запустить с его помощью процесс, ни даже конвертировать его в основной (primary) токен с помощью функции DuplicateToken вы не сможете.
И только если у вас есть привилегия SeTcbPrivilege, linked-токен, возвращённый функцией GetTokenInfromation, оказывается основным токеном. Вот только по умолчанию этой привилегией обладает единственный пользователь в системе - Local System. И даже если вы выдадите её своему пользователю, вопреки всем рекомендациям, советующим не делать этого ни при каких условиях, она точно будет ограничена UAC`ом при интерактивном входе в систему. Единственное место, где вы имеете шанс воспользоваться подобной возможностью - это сервис, работающий под учётной записью Local System. Надо отдать должное UAC`у - даже там вам могут пригодиться подобные приседания.
Комментариев нет:
Отправить комментарий