Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Есть разные способы злоупотреблять сессией пользователя на устройстве: кража учетных данных, манипуляции с токенами и другие. Но знаешь ли ты, что можно сымитировать получение TGT-билета пользователем через совершенно легитимные функции Windows? warning
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
В этом году появилось несколько новых способов из такого разряда. Наиболее интересные — WTSImpersonator и GIUDA. Последний позволяет получать тикеты залогиненного пользователя, даже не зная его пароля! Давай разберемся, как это работает, а параллельно напишем реализацию на C++, которую я назвал TGSThief.
При входе пользователя в Windows появляется сессия пользователя, которая хранит все данные о нем. Для каждого нового пользователя создается новая сессия. Например, если на компьютере одновременно работают два пользователя, то будет две сессии.
Как выглядят Logon Sessions
Каждая сессия определяется с помощью LUID (locally unique identifier). Из названия понятно, что LUID уникален для каждой сессии. Информация хранится в виде одноименной структуры.
typedef struct _LUID { ULONG LowPart; LONG HighPart;} LUID, *PLUID;
Сам LUID представлен в виде двух значений: ULONG и LONG. Причем обычно заполняется лишь поле LowPart
, а HighPart
имеет значение 0.
Эта структура используется во всех функциях WinAPI, которые так или иначе связаны с сессиями пользователя.
С помощью GetTokenInformation() можно получить LUID пользователя. Для этого функции следует передать токен процесса, запущенного от имени текущего пользователя.
#include <windows.h>#include <iostream>#include <sddl.h>int main() { HANDLE tokenHandle; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &tokenHandle)) { std::cerr << "Ошибка OpenProcessToken: " << GetLastError() << std::endl; return 1; } DWORD tokenInformationLength = 0; GetTokenInformation(tokenHandle, TokenStatistics, nullptr, 0, &tokenInformationLength); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { std::cerr << "GetTokenInformation неудачный первый вызов: " << GetLastError() << std::endl; CloseHandle(tokenHandle); return 1; } TOKEN_STATISTICS* tokenStats = reinterpret_cast<TOKEN_STATISTICS*>(new BYTE[tokenInformationLength]); if (tokenStats == nullptr) { std::cerr << "Ошибка выделения памяти для TOKEN_STATISTICS" << std::endl; CloseHandle(tokenHandle); return 1; } if (!GetTokenInformation(tokenHandle, TokenStatistics, tokenStats, tokenInformationLength, &tokenInformationLength)) { std::cerr << "Ошибка GetTokenInformation: " << GetLastError() << std::endl; delete[] tokenStats; CloseHandle(tokenHandle); return 1; } std::cout << "LUID: " << std::hex << "0x" << std::uppercase << tokenStats->AuthenticationId.LowPart << tokenStats->AuthenticationId.HighPart << std::endl; delete[] tokenStats; CloseHandle(tokenHandle); return 0;}
Информация о LUID пользователя упадет в поле AuthenticationId
структуры TOKEN_STATISTICS
.
typedef struct _TOKEN_STATISTICS { LUID TokenId; LUID AuthenticationId; LARGE_INTEGER ExpirationTime; TOKEN_TYPE TokenType; SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; DWORD DynamicCharged; DWORD DynamicAvailable; DWORD GroupCount; DWORD PrivilegeCount; LUID ModifiedId;} TOKEN_STATISTICS, *PTOKEN_STATISTICS;
Именно на манипуляциях с LUID основана логика инструмента GIUDA. Фактически идет подмена LUID, что открывает возможность запросить билет от лица пользователя, чья сессия просто присутствует на устройстве. Свой LUID мы получать научились, а как получать LUID других пользователей?
Источник: xakep.ru